[
  {
    "path": ".editorconfig",
    "content": "[*]\ncharset=utf-8\nend_of_line=lf\ninsert_final_newline=false\nindent_style=space\nindent_size=2\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.dec\ttext\n"
  },
  {
    "path": ".gitignore",
    "content": "/.idea/\n/.gradle/\n/build/\n/out/"
  },
  {
    "path": "README.md",
    "content": "### About Fernflower\n\nFernflower is the first actually working analytical decompiler for Java and \nprobably for a high-level programming language in general. Naturally it is still \nunder development, please send your bug reports and improvement suggestions to the\n[issue tracker](https://youtrack.jetbrains.com/newIssue?project=IDEA&clearDraft=true&c=Subsystem+Decompiler).\n\n### Licence\n\nFernflower is licenced under the [Apache Licence Version 2.0](http://www.apache.org/licenses/LICENSE-2.0).\n\n### Running from command line\n\n`java -jar fernflower.jar [-<option>=<value>]* [<source>]+ <destination>`\n\n\\* means 0 or more times\\\n\\+ means 1 or more times\n\n\\<source>: file or directory with files to be decompiled. Directories are recursively scanned. Allowed file extensions are class, zip and jar.\n          Sources prefixed with -e= mean \"library\" files that won't be decompiled, but taken into account when analysing relationships between \n          classes or methods. Especially renaming of identifiers (s. option 'ren') can benefit from information about external classes.          \n\n\\<destination>: destination directory \n\n\\<option>, \\<value>: a command-line option with the corresponding value (see \"Command-line options\" below).\n\n##### Examples:\n\n`java -jar fernflower.jar -hes=0 -hdc=0 c:\\Temp\\binary\\ -e=c:\\Java\\rt.jar c:\\Temp\\source\\`\n\n`java -jar fernflower.jar -dgs=1 c:\\Temp\\binary\\library.jar c:\\Temp\\binary\\Boot.class c:\\Temp\\source\\`\n\n### Command-line options\n\nWith the exception of mpm and urc the value of 1 means the option is activated, 0 - deactivated. Default \nvalue, if any, is given between parentheses.\n\nTypically, the following options will be changed by user, if any: hes, hdc, dgs, mpm, ren, urc \nThe rest of options can be left as they are: they are aimed at professional reverse engineers.\n\n- rbr (1): hide bridge methods\n- rsy (0): hide synthetic class members\n- din (1): decompile inner classes\n- dc4 (1): collapse 1.4 class references\n- das (1): decompile assertions\n- hes (1): hide empty super invocation\n- hdc (1): hide empty default constructor\n- dgs (0): decompile generic signatures\n- ner (1): assume return not throwing exceptions\n- den (1): decompile enumerations\n- rgn (1): remove getClass() invocation, when it is part of a qualified new statement\n- lit (0): output numeric literals \"as-is\"\n- asc (0): encode non-ASCII characters in string and character literals as Unicode escapes\n- bto (1): interpret int 1 as boolean true (workaround to a compiler bug)\n- nns (0): allow for not set synthetic attribute (workaround to a compiler bug)\n- uto (1): consider nameless types as java.lang.Object (workaround to a compiler architecture flaw)\n- udv (1): reconstruct variable names from debug information, if present\n- ump (1): reconstruct parameter names from corresponding attributes, if present\n- rer (1): remove empty exception ranges\n- fdi (1): de-inline finally structures\n- mpm (0): maximum allowed processing time per decompiled method, in seconds. 0 means no upper limit\n- ren (0): rename ambiguous (resp. obfuscated) classes and class elements\n- urc (-): full name of a user-supplied class implementing IIdentifierRenamer interface. It is used to determine which class identifiers\n           should be renamed and provides new identifier names (see \"Renaming identifiers\")\n- inn (1): check for IntelliJ IDEA-specific @NotNull annotation and remove inserted code if found\n- lac (0): decompile lambda expressions to anonymous classes\n- nls (0): define new line character to be used for output. 0 - '\\r\\n' (Windows), 1 - '\\n' (Unix), default is OS-dependent\n- ind: indentation string (default is 3 spaces)\n- crp (0): use record patterns where it is possible\n- cps (0): use switch with patterns where it is possible \n- log (INFO): a logging level, possible values are TRACE, INFO, WARN, ERROR\n\n### Renaming identifiers\n\nSome obfuscators give classes and their member elements short, meaningless and above all ambiguous names. Recompiling of such\ncode leads to a great number of conflicts. Therefore it is advisable to let the decompiler rename elements in its turn, \nensuring uniqueness of each identifier.\n\nOption 'ren' (i.e. -ren=1) activates renaming functionality. Default renaming strategy goes as follows:\n- rename an element if its name is a reserved word or is shorter than 3 characters\n- new names are built according to a simple pattern: (class|method|field)_\\<consecutive unique number>  \nYou can overwrite this rules by providing your own implementation of the 4 key methods invoked by the decompiler while renaming. Simply \npass a class that implements org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer in the option 'urc'\n(e.g. -urc=com.example.MyRenamer) to Fernflower. The class must be available on the application classpath.\n\nThe meaning of each method should be clear from naming: toBeRenamed determine whether the element will be renamed, while the other three\nprovide new names for classes, methods and fields respectively.  \n"
  },
  {
    "path": "build.gradle",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\napply plugin: 'java'\n\ncompileJava {\n  sourceCompatibility '17'\n  targetCompatibility '17'\n}\n\nsourceSets {\n  main.java.srcDirs 'src'\n  test.java.srcDirs 'test'\n}\n\nrepositories { mavenCentral() }\ndependencies {\n  implementation 'org.jetbrains:annotations:20.1.0'\n  testImplementation 'junit:junit:4.12'\n  testImplementation 'org.assertj:assertj-core:3.23.1'\n}\n\njar {\n  archiveFileName = 'fernflower.jar'\n  manifest {\n    attributes 'Main-Class': 'org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler'\n  }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.6.3-bin.zip\nnetworkTimeout=10000\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\nAPP_HOME=$( cd \"${APP_HOME:-./}\" && pwd -P ) || exit\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC3045 \n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n# Collect all arguments for the java command;\n#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of\n#     shell script including quotes and variable substitutions, so put them in\n#     double quotes to make sure that they get re-expanded; and\n#   * put everything else in single quotes, so that it's not re-expanded.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\"==\"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\n@rem This is normally unused\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif %ERRORLEVEL% equ 0 goto execute\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif %ERRORLEVEL% equ 0 goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nset EXIT_CODE=%ERRORLEVEL%\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\nexit /b %EXIT_CODE%\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "intellij.java.decompiler.engine.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/src\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/test\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/resources\" type=\"java-resource\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" scope=\"TEST\" name=\"JUnit4\" level=\"project\" />\n    <orderEntry type=\"library\" scope=\"TEST\" name=\"assertJ\" level=\"project\" />\n    <orderEntry type=\"library\" name=\"jetbrains-annotations\" level=\"project\" />\n  </component>\n</module>"
  },
  {
    "path": "resources/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nMain-Class: org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler\nAutomatic-Module-Name: org.jetbrains.java.decompiler\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/ClassNameConstants.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler;\n\npublic interface ClassNameConstants {\n  String JAVA_LANG_STRING = \"java/lang/String\";\n  String JAVA_LANG_CHARACTER = \"java/lang/Character\";\n  String JAVA_UTIL_OBJECTS = \"java/util/Objects\";\n  String JAVA_LANG_OBJECT = \"java/lang/Object\";\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/CodeConstants.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code;\n\n@SuppressWarnings({\"unused\", \"SpellCheckingInspection\"})\npublic interface CodeConstants {\n  // ----------------------------------------------------------------------\n  // BYTECODE VERSIONS\n  // ----------------------------------------------------------------------\n\n  int BYTECODE_JAVA_LE_4 = 48;\n  int BYTECODE_JAVA_5 = 49;\n  int BYTECODE_JAVA_6 = 50;\n  int BYTECODE_JAVA_7 = 51;\n  int BYTECODE_JAVA_8 = 52;\n  int BYTECODE_JAVA_9 = 53;\n  int BYTECODE_JAVA_10 = 54;\n  int BYTECODE_JAVA_11 = 55;\n  int BYTECODE_JAVA_12 = 56;\n  int BYTECODE_JAVA_13 = 57;\n  int BYTECODE_JAVA_14 = 58;\n  int BYTECODE_JAVA_15 = 59;\n  int BYTECODE_JAVA_16 = 60;\n  int BYTECODE_JAVA_17 = 61;\n  int BYTECODE_JAVA_18 = 62;\n  int BYTECODE_JAVA_19 = 63;\n  int BYTECODE_JAVA_20 = 64;\n  int BYTECODE_JAVA_21 = 65;\n  int BYTECODE_JAVA_22 = 66;\n\n  // ----------------------------------------------------------------------\n  // VARIABLE TYPES\n  // ----------------------------------------------------------------------\n\n  int TYPE_BYTE = 0;\n  int TYPE_CHAR = 1;\n  int TYPE_DOUBLE = 2;\n  int TYPE_FLOAT = 3;\n  int TYPE_INT = 4;\n  int TYPE_LONG = 5;\n  int TYPE_SHORT = 6;\n  int TYPE_BOOLEAN = 7;\n  int TYPE_OBJECT = 8;\n  int TYPE_ADDRESS = 9;\n  int TYPE_VOID = 10;\n  int TYPE_ANY = 11;\n  int TYPE_GROUP2EMPTY = 12;\n  int TYPE_NULL = 13;\n  int TYPE_NOTINITIALIZED = 14;\n  int TYPE_BYTECHAR = 15;\n  int TYPE_SHORTCHAR = 16;\n  int TYPE_UNKNOWN = 17;\n  int TYPE_GENVAR = 18;\n\n  // ----------------------------------------------------------------------\n  // VARIABLE TYPE FAMILIES\n  // ----------------------------------------------------------------------\n\n  int TYPE_FAMILY_UNKNOWN = 0;\n  int TYPE_FAMILY_BOOLEAN = 1;\n  int TYPE_FAMILY_INTEGER = 2;\n  int TYPE_FAMILY_FLOAT = 3;\n  int TYPE_FAMILY_LONG = 4;\n  int TYPE_FAMILY_DOUBLE = 5;\n  int TYPE_FAMILY_OBJECT = 6;\n\n  // ----------------------------------------------------------------------\n  // ACCESS FLAGS\n  // ----------------------------------------------------------------------\n\n  int ACC_PUBLIC = 0x0001;\n  int ACC_PRIVATE = 0x0002;\n  int ACC_PROTECTED = 0x0004;\n  int ACC_STATIC = 0x0008;\n  int ACC_FINAL = 0x0010;\n  int ACC_SYNCHRONIZED = 0x0020;\n  int ACC_OPEN = 0x0020;\n  int ACC_TRANSITIVE = 0x0020;\n  int ACC_VOLATILE = 0x0040;\n  int ACC_BRIDGE = 0x0040;\n  int ACC_STATIC_PHASE = 0x0040;\n  int ACC_TRANSIENT = 0x0080;\n  int ACC_VARARGS = 0x0080;\n  int ACC_NATIVE = 0x0100;\n  int ACC_ABSTRACT = 0x0400;\n  int ACC_STRICT = 0x0800;\n  int ACC_SYNTHETIC = 0x1000;\n  int ACC_ANNOTATION = 0x2000;\n  int ACC_ENUM = 0x4000;\n  int ACC_MANDATED = 0x8000;\n  int ACC_MODULE = 0x8000;\n\n  // ----------------------------------------------------------------------\n  // CLASS FLAGS\n  // ----------------------------------------------------------------------\n\n  int ACC_SUPER = 0x0020;\n  int ACC_INTERFACE = 0x0200;\n\n  // ----------------------------------------------------------------------\n  // INSTRUCTION GROUPS\n  // ----------------------------------------------------------------------\n\n  int GROUP_GENERAL = 1;\n  int GROUP_JUMP = 2;\n  int GROUP_SWITCH = 3;\n  int GROUP_INVOCATION = 4;\n  int GROUP_FIELDACCESS = 5;\n  int GROUP_RETURN = 6;\n\n  // ----------------------------------------------------------------------\n  // POOL CONSTANTS\n  // ----------------------------------------------------------------------\n\n  int CONSTANT_Utf8 = 1;\n  int CONSTANT_Integer = 3;\n  int CONSTANT_Float = 4;\n  int CONSTANT_Long = 5;\n  int CONSTANT_Double = 6;\n  int CONSTANT_Class = 7;\n  int CONSTANT_String = 8;\n  int CONSTANT_Fieldref = 9;\n  int CONSTANT_Methodref = 10;\n  int CONSTANT_InterfaceMethodref = 11;\n  int CONSTANT_NameAndType = 12;\n  int CONSTANT_MethodHandle = 15;\n  int CONSTANT_MethodType = 16;\n  int CONSTANT_Dynamic = 17;\n  int CONSTANT_InvokeDynamic = 18;\n  int CONSTANT_Module = 19;\n  int CONSTANT_Package = 20;\n\n  // ----------------------------------------------------------------------\n  // MethodHandle reference_kind values\n  // ----------------------------------------------------------------------\n\n  int CONSTANT_MethodHandle_REF_getField = 1;\n  int CONSTANT_MethodHandle_REF_getStatic = 2;\n  int CONSTANT_MethodHandle_REF_putField = 3;\n  int CONSTANT_MethodHandle_REF_putStatic = 4;\n  int CONSTANT_MethodHandle_REF_invokeVirtual = 5;\n  int CONSTANT_MethodHandle_REF_invokeStatic = 6;\n  int CONSTANT_MethodHandle_REF_invokeSpecial = 7;\n  int CONSTANT_MethodHandle_REF_newInvokeSpecial = 8;\n  int CONSTANT_MethodHandle_REF_invokeInterface = 9;\n\n  // ----------------------------------------------------------------------\n  // VM OPCODES\n  // ----------------------------------------------------------------------\n\n  int opc_nop = 0;\n  int opc_aconst_null = 1;\n  int opc_iconst_m1 = 2;\n  int opc_iconst_0 = 3;\n  int opc_iconst_1 = 4;\n  int opc_iconst_2 = 5;\n  int opc_iconst_3 = 6;\n  int opc_iconst_4 = 7;\n  int opc_iconst_5 = 8;\n  int opc_lconst_0 = 9;\n  int opc_lconst_1 = 10;\n  int opc_fconst_0 = 11;\n  int opc_fconst_1 = 12;\n  int opc_fconst_2 = 13;\n  int opc_dconst_0 = 14;\n  int opc_dconst_1 = 15;\n  int opc_bipush = 16;\n  int opc_sipush = 17;\n  int opc_ldc = 18;\n  int opc_ldc_w = 19;\n  int opc_ldc2_w = 20;\n  int opc_iload = 21;\n  int opc_lload = 22;\n  int opc_fload = 23;\n  int opc_dload = 24;\n  int opc_aload = 25;\n  int opc_iload_0 = 26;\n  int opc_iload_1 = 27;\n  int opc_iload_2 = 28;\n  int opc_iload_3 = 29;\n  int opc_lload_0 = 30;\n  int opc_lload_1 = 31;\n  int opc_lload_2 = 32;\n  int opc_lload_3 = 33;\n  int opc_fload_0 = 34;\n  int opc_fload_1 = 35;\n  int opc_fload_2 = 36;\n  int opc_fload_3 = 37;\n  int opc_dload_0 = 38;\n  int opc_dload_1 = 39;\n  int opc_dload_2 = 40;\n  int opc_dload_3 = 41;\n  int opc_aload_0 = 42;\n  int opc_aload_1 = 43;\n  int opc_aload_2 = 44;\n  int opc_aload_3 = 45;\n  int opc_iaload = 46;\n  int opc_laload = 47;\n  int opc_faload = 48;\n  int opc_daload = 49;\n  int opc_aaload = 50;\n  int opc_baload = 51;\n  int opc_caload = 52;\n  int opc_saload = 53;\n  int opc_istore = 54;\n  int opc_lstore = 55;\n  int opc_fstore = 56;\n  int opc_dstore = 57;\n  int opc_astore = 58;\n  int opc_istore_0 = 59;\n  int opc_istore_1 = 60;\n  int opc_istore_2 = 61;\n  int opc_istore_3 = 62;\n  int opc_lstore_0 = 63;\n  int opc_lstore_1 = 64;\n  int opc_lstore_2 = 65;\n  int opc_lstore_3 = 66;\n  int opc_fstore_0 = 67;\n  int opc_fstore_1 = 68;\n  int opc_fstore_2 = 69;\n  int opc_fstore_3 = 70;\n  int opc_dstore_0 = 71;\n  int opc_dstore_1 = 72;\n  int opc_dstore_2 = 73;\n  int opc_dstore_3 = 74;\n  int opc_astore_0 = 75;\n  int opc_astore_1 = 76;\n  int opc_astore_2 = 77;\n  int opc_astore_3 = 78;\n  int opc_iastore = 79;\n  int opc_lastore = 80;\n  int opc_fastore = 81;\n  int opc_dastore = 82;\n  int opc_aastore = 83;\n  int opc_bastore = 84;\n  int opc_castore = 85;\n  int opc_sastore = 86;\n  int opc_pop = 87;\n  int opc_pop2 = 88;\n  int opc_dup = 89;\n  int opc_dup_x1 = 90;\n  int opc_dup_x2 = 91;\n  int opc_dup2 = 92;\n  int opc_dup2_x1 = 93;\n  int opc_dup2_x2 = 94;\n  int opc_swap = 95;\n  int opc_iadd = 96;\n  int opc_ladd = 97;\n  int opc_fadd = 98;\n  int opc_dadd = 99;\n  int opc_isub = 100;\n  int opc_lsub = 101;\n  int opc_fsub = 102;\n  int opc_dsub = 103;\n  int opc_imul = 104;\n  int opc_lmul = 105;\n  int opc_fmul = 106;\n  int opc_dmul = 107;\n  int opc_idiv = 108;\n  int opc_ldiv = 109;\n  int opc_fdiv = 110;\n  int opc_ddiv = 111;\n  int opc_irem = 112;\n  int opc_lrem = 113;\n  int opc_frem = 114;\n  int opc_drem = 115;\n  int opc_ineg = 116;\n  int opc_lneg = 117;\n  int opc_fneg = 118;\n  int opc_dneg = 119;\n  int opc_ishl = 120;\n  int opc_lshl = 121;\n  int opc_ishr = 122;\n  int opc_lshr = 123;\n  int opc_iushr = 124;\n  int opc_lushr = 125;\n  int opc_iand = 126;\n  int opc_land = 127;\n  int opc_ior = 128;\n  int opc_lor = 129;\n  int opc_ixor = 130;\n  int opc_lxor = 131;\n  int opc_iinc = 132;\n  int opc_i2l = 133;\n  int opc_i2f = 134;\n  int opc_i2d = 135;\n  int opc_l2i = 136;\n  int opc_l2f = 137;\n  int opc_l2d = 138;\n  int opc_f2i = 139;\n  int opc_f2l = 140;\n  int opc_f2d = 141;\n  int opc_d2i = 142;\n  int opc_d2l = 143;\n  int opc_d2f = 144;\n  int opc_i2b = 145;\n  int opc_i2c = 146;\n  int opc_i2s = 147;\n  int opc_lcmp = 148;\n  int opc_fcmpl = 149;\n  int opc_fcmpg = 150;\n  int opc_dcmpl = 151;\n  int opc_dcmpg = 152;\n  int opc_ifeq = 153;\n  int opc_ifne = 154;\n  int opc_iflt = 155;\n  int opc_ifge = 156;\n  int opc_ifgt = 157;\n  int opc_ifle = 158;\n  int opc_if_icmpeq = 159;\n  int opc_if_icmpne = 160;\n  int opc_if_icmplt = 161;\n  int opc_if_icmpge = 162;\n  int opc_if_icmpgt = 163;\n  int opc_if_icmple = 164;\n  int opc_if_acmpeq = 165;\n  int opc_if_acmpne = 166;\n  int opc_goto = 167;\n  int opc_jsr = 168;\n  int opc_ret = 169;\n  int opc_tableswitch = 170;\n  int opc_lookupswitch = 171;\n  int opc_ireturn = 172;\n  int opc_lreturn = 173;\n  int opc_freturn = 174;\n  int opc_dreturn = 175;\n  int opc_areturn = 176;\n  int opc_return = 177;\n  int opc_getstatic = 178;\n  int opc_putstatic = 179;\n  int opc_getfield = 180;\n  int opc_putfield = 181;\n  int opc_invokevirtual = 182;\n  int opc_invokespecial = 183;\n  int opc_invokestatic = 184;\n  int opc_invokeinterface = 185;\n  int opc_invokedynamic = 186;\n  int opc_new = 187;\n  int opc_newarray = 188;\n  int opc_anewarray = 189;\n  int opc_arraylength = 190;\n  int opc_athrow = 191;\n  int opc_checkcast = 192;\n  int opc_instanceof = 193;\n  int opc_monitorenter = 194;\n  int opc_monitorexit = 195;\n  int opc_wide = 196;\n  int opc_multianewarray = 197;\n  int opc_ifnull = 198;\n  int opc_ifnonnull = 199;\n  int opc_goto_w = 200;\n  int opc_jsr_w = 201;\n\n  String CLINIT_NAME = \"<clinit>\";\n  String INIT_NAME = \"<init>\";\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/ExceptionHandler.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\n\npublic class ExceptionHandler {\n  public int from = 0;\n  public int to = 0;\n  public int handler = 0;\n\n  public int from_instr = 0;\n  public int to_instr = 0;\n  public int handler_instr = 0;\n\n  public String exceptionClass = null;\n\n  public String toString() {\n    String new_line_separator = DecompilerContext.getNewLineSeparator();\n    return \"from: \" + from + \" to: \" + to + \" handler: \" + handler + new_line_separator +\n           \"from_instr: \" + from_instr + \" to_instr: \" + to_instr + \" handler_instr: \" + handler_instr + new_line_separator +\n           \"exceptionClass: \" + exceptionClass + new_line_separator;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/ExceptionTable.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic class ExceptionTable {\n  public static final ExceptionTable EMPTY = new ExceptionTable(Collections.emptyList());\n\n  private final List<ExceptionHandler> handlers;\n\n  public ExceptionTable(List<ExceptionHandler> handlers) {\n    this.handlers = handlers;\n  }\n\n  public List<ExceptionHandler> getHandlers() {\n    return handlers;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code;\n\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\n\npublic class FullInstructionSequence extends InstructionSequence {\n\n  // *****************************************************************************\n  // constructors\n  // *****************************************************************************\n\n  public FullInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr, ExceptionTable extable) {\n    super(collinstr);\n    this.exceptionTable = extable;\n\n    // translate raw exception handlers to instr\n    for (ExceptionHandler handler : extable.getHandlers()) {\n      handler.from_instr = this.getPointerByAbsOffset(handler.from);\n      handler.to_instr = this.getPointerByAbsOffset(handler.to);\n      handler.handler_instr = this.getPointerByAbsOffset(handler.handler);\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/Instruction.java",
    "content": "// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.code;\n\nimport org.jetbrains.java.decompiler.util.TextUtil;\n\npublic class Instruction {\n  public static Instruction create(int opcode, boolean wide, int group, int bytecodeVersion, int[] operands) {\n    if (opcode >= CodeConstants.opc_ifeq && opcode <= CodeConstants.opc_if_acmpne ||\n        opcode == CodeConstants.opc_ifnull || opcode == CodeConstants.opc_ifnonnull ||\n        opcode == CodeConstants.opc_jsr || opcode == CodeConstants.opc_jsr_w ||\n        opcode == CodeConstants.opc_goto || opcode == CodeConstants.opc_goto_w) {\n      return new JumpInstruction(opcode, group, wide, bytecodeVersion, operands);\n    }\n    else if (opcode == CodeConstants.opc_tableswitch || opcode == CodeConstants.opc_lookupswitch) {\n      return new SwitchInstruction(opcode, group, wide, bytecodeVersion, operands);\n    }\n    else {\n      return new Instruction(opcode, group, wide, bytecodeVersion, operands);\n    }\n  }\n\n  public static boolean equals(Instruction i1, Instruction i2) {\n    return i1 != null && i2 != null &&\n           (i1 == i2 ||\n            i1.opcode == i2.opcode &&\n            i1.wide == i2.wide &&\n            i1.operandsCount() == i2.operandsCount());\n  }\n\n  public final int opcode;\n  public final int group;\n  public final boolean wide;\n  public final int bytecodeVersion;\n\n  protected final int[] operands;\n\n  public Instruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {\n    this.opcode = opcode;\n    this.group = group;\n    this.wide = wide;\n    this.bytecodeVersion = bytecodeVersion;\n    this.operands = operands;\n  }\n\n  public void initInstruction(InstructionSequence seq) { }\n\n  public int operandsCount() {\n    return operands == null ? 0 : operands.length;\n  }\n\n  public int operand(int index) {\n    return operands[index];\n  }\n\n  public boolean canFallThrough() {\n    return opcode != CodeConstants.opc_goto && opcode != CodeConstants.opc_goto_w && opcode != CodeConstants.opc_ret &&\n           !(opcode >= CodeConstants.opc_ireturn && opcode <= CodeConstants.opc_return) &&\n           opcode != CodeConstants.opc_athrow &&\n           opcode != CodeConstants.opc_jsr && opcode != CodeConstants.opc_tableswitch && opcode != CodeConstants.opc_lookupswitch;\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder res = new StringBuilder();\n    if (wide) res.append(\"@wide \");\n    res.append(\"@\").append(TextUtil.getInstructionName(opcode));\n\n    int len = operandsCount();\n    for (int i = 0; i < len; i++) {\n      int op = operands[i];\n      if (op < 0) {\n        res.append(\" -\").append(Integer.toHexString(-op));\n      }\n      else {\n        res.append(\" \").append(Integer.toHexString(op));\n      }\n    }\n\n    return res.toString();\n  }\n\n  @Override\n  @SuppressWarnings(\"MethodDoesntCallSuperMethod\")\n  public Instruction clone() {\n    return create(opcode, wide, group, bytecodeVersion, operands == null ? null : operands.clone());\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/InstructionSequence.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.util.TextUtil;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\npublic abstract class InstructionSequence {\n\n  // *****************************************************************************\n  // private fields\n  // *****************************************************************************\n\n  protected final VBStyleCollection<Instruction, Integer> collinstr;\n\n  protected int pointer = 0;\n\n  protected ExceptionTable exceptionTable = ExceptionTable.EMPTY;\n\n  protected InstructionSequence() {\n    this(new VBStyleCollection<>());\n  }\n\n  protected InstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) {\n    this.collinstr = collinstr;\n  }\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  // to nbe overwritten\n  @Override\n  public InstructionSequence clone() {\n    return null;\n  }\n\n  public void clear() {\n    collinstr.clear();\n    pointer = 0;\n    exceptionTable = ExceptionTable.EMPTY;\n  }\n\n  public void addInstruction(Instruction inst, int offset) {\n    collinstr.addWithKey(inst, offset);\n  }\n\n  public void addInstruction(int index, Instruction inst, int offset) {\n    collinstr.addWithKeyAndIndex(index, inst, offset);\n  }\n\n  public void addSequence(InstructionSequence seq) {\n    for (int i = 0; i < seq.length(); i++) {\n      addInstruction(seq.getInstr(i), -1); // TODO: any sensible value possible?\n    }\n  }\n\n  public void removeInstruction(int index) {\n    collinstr.remove(index);\n  }\n\n  public void removeLast() {\n    if (!collinstr.isEmpty()) {\n      collinstr.remove(collinstr.size() - 1);\n    }\n  }\n\n  public Instruction getInstr(int index) {\n  return collinstr.get(index);\n  }\n\n  public Instruction getLastInstr() {\n  return collinstr.getLast();\n  }\n\n  public int getOffset(int index) {\n    return collinstr.getKey(index);\n  }\n\n  public int getPointerByAbsOffset(int offset) {\n    Integer absoffset = offset;\n    if (collinstr.containsKey(absoffset)) {\n      return collinstr.getIndexByKey(absoffset);\n    }\n    else {\n      return -1;\n    }\n  }\n\n  public int getPointerByRelOffset(int offset) {\n    Integer absoffset = collinstr.getKey(pointer) + offset;\n    if (collinstr.containsKey(absoffset)) {\n      return collinstr.getIndexByKey(absoffset);\n    }\n    else {\n      return -1;\n    }\n  }\n\n  public int length() {\n    return collinstr.size();\n  }\n\n  public boolean isEmpty() {\n    return collinstr.isEmpty();\n  }\n\n  public void addToPointer(int diff) {\n    this.pointer += diff;\n  }\n\n  public String toString() {\n    return toString(0);\n  }\n\n  public String toString(int indent) {\n\n    String new_line_separator = DecompilerContext.getNewLineSeparator();\n\n    StringBuilder buf = new StringBuilder();\n\n    for (int i = 0; i < collinstr.size(); i++) {\n    buf.append(TextUtil.getIndentString(indent));\n      buf.append(collinstr.getKey(i).intValue());\n      buf.append(\": \");\n      buf.append(collinstr.get(i).toString());\n      buf.append(new_line_separator);\n    }\n\n    return buf.toString();\n  }\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public int getPointer() {\n    return pointer;\n  }\n\n  public void setPointer(int pointer) {\n    this.pointer = pointer;\n  }\n\n  public ExceptionTable getExceptionTable() {\n    return exceptionTable;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/JumpInstruction.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code;\n\npublic class JumpInstruction extends Instruction {\n  public int destination;\n\n  public JumpInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {\n    super(opcode, group, wide, bytecodeVersion, operands);\n  }\n\n  @Override\n  public void initInstruction(InstructionSequence seq) {\n    destination = seq.getPointerByRelOffset(this.operand(0));\n  }\n\n  @Override\n  public JumpInstruction clone() {\n    JumpInstruction copy = (JumpInstruction)super.clone();\n    copy.destination = destination;\n    return copy;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code;\n\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\npublic class SimpleInstructionSequence extends InstructionSequence {\n\n  public SimpleInstructionSequence() {\n  }\n\n  public SimpleInstructionSequence(VBStyleCollection<Instruction, Integer> collinstr) {\n    super(collinstr);\n  }\n\n  @Override\n  public SimpleInstructionSequence clone() {\n    SimpleInstructionSequence newseq = new SimpleInstructionSequence(collinstr.clone());\n    newseq.setPointer(this.getPointer());\n\n    return newseq;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/SwitchInstruction.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code;\n\npublic class SwitchInstruction extends Instruction {\n  private int[] destinations;\n  private int[] values;\n  private int defaultDestination;\n\n  public SwitchInstruction(int opcode, int group, boolean wide, int bytecodeVersion, int[] operands) {\n    super(opcode, group, wide, bytecodeVersion, operands);\n  }\n\n  @Override\n  public void initInstruction(InstructionSequence seq) {\n    defaultDestination = seq.getPointerByRelOffset(operands[0]);\n\n    int prefix = opcode == CodeConstants.opc_tableswitch ? 3 : 2;\n    int len = operands.length - prefix;\n    int low = 0;\n    if (opcode == CodeConstants.opc_lookupswitch) {\n      len /= 2;\n    }\n    else {\n      low = operands[1];\n    }\n\n    destinations = new int[len];\n    values = new int[len];\n    for (int i = 0, k = 0; i < len; i++, k++) {\n      if (opcode == CodeConstants.opc_lookupswitch) {\n        values[i] = operands[prefix + k];\n        k++;\n      }\n      else {\n        values[i] = low + k;\n      }\n      destinations[i] = seq.getPointerByRelOffset(operands[prefix + k]);\n    }\n  }\n\n  public int[] getDestinations() {\n    return destinations;\n  }\n\n  public int[] getValues() {\n    return values;\n  }\n\n  public int getDefaultDestination() {\n    return defaultDestination;\n  }\n\n  @Override\n  public SwitchInstruction clone() {\n    SwitchInstruction copy = (SwitchInstruction)super.clone();\n    copy.defaultDestination = defaultDestination;\n    copy.destinations = destinations.clone();\n    copy.values = values.clone();\n    return copy;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code.cfg;\n\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.code.SimpleInstructionSequence;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class BasicBlock implements IGraphNode {\n  public final int id;\n\n  private final InstructionSequence seq;\n  private final List<Integer> originalOffsets = new ArrayList<>();\n  private final List<BasicBlock> predecessors = new ArrayList<>();\n  private final List<BasicBlock> successors = new ArrayList<>();\n  private final List<BasicBlock> predecessorExceptions = new ArrayList<>();\n  private final List<BasicBlock> successorExceptions = new ArrayList<>();\n\n  public int mark = 0;\n\n  public BasicBlock(int id) {\n    this(id, new SimpleInstructionSequence());\n  }\n\n  public BasicBlock(int id, InstructionSequence seq) {\n    this.id = id;\n    this.seq = seq;\n  }\n\n  public BasicBlock clone(int newId) {\n    BasicBlock block = new BasicBlock(newId, seq.clone());\n    block.originalOffsets.addAll(originalOffsets);\n    return block;\n  }\n\n  public Instruction getInstruction(int index) {\n    return seq.getInstr(index);\n  }\n\n  public Instruction getLastInstruction() {\n    return seq.isEmpty() ? null : seq.getLastInstr();\n  }\n\n  public Integer getOriginalOffset(int index) {\n    return index < originalOffsets.size() ? originalOffsets.get(index) : Integer.valueOf(-1);\n  }\n\n  public int size() {\n    return seq.length();\n  }\n\n  public void addPredecessor(BasicBlock block) {\n    predecessors.add(block);\n  }\n\n  public void removePredecessor(BasicBlock block) {\n    while (predecessors.remove(block)) /**/;\n  }\n\n  public void addSuccessor(BasicBlock block) {\n    successors.add(block);\n    block.addPredecessor(this);\n  }\n\n  public void removeSuccessor(BasicBlock block) {\n    while (successors.remove(block)) /**/;\n    block.removePredecessor(this);\n  }\n\n  // FIXME: unify block comparisons: id or direct equality\n  public void replaceSuccessor(BasicBlock oldBlock, BasicBlock newBlock) {\n    for (int i = 0; i < successors.size(); i++) {\n      if (successors.get(i).id == oldBlock.id) {\n        successors.set(i, newBlock);\n        oldBlock.removePredecessor(this);\n        newBlock.addPredecessor(this);\n      }\n    }\n\n    for (int i = 0; i < successorExceptions.size(); i++) {\n      if (successorExceptions.get(i).id == oldBlock.id) {\n        successorExceptions.set(i, newBlock);\n        oldBlock.removePredecessorException(this);\n        newBlock.addPredecessorException(this);\n      }\n    }\n  }\n\n  public void addPredecessorException(BasicBlock block) {\n    predecessorExceptions.add(block);\n  }\n\n  public void removePredecessorException(BasicBlock block) {\n    while (predecessorExceptions.remove(block)) /**/;\n  }\n\n  public void addSuccessorException(BasicBlock block) {\n    if (!successorExceptions.contains(block)) {\n      successorExceptions.add(block);\n      block.addPredecessorException(this);\n    }\n  }\n\n  public void removeSuccessorException(BasicBlock block) {\n    while (successorExceptions.remove(block)) /**/;\n    block.removePredecessorException(this);\n  }\n\n  public boolean isSuccessor(BasicBlock block) {\n    return successors.stream().anyMatch(successor -> successor.id == block.id);\n  }\n\n  public List<Integer> getOriginalOffsets() {\n    return originalOffsets;\n  }\n\n  public InstructionSequence getSeq() {\n    return seq;\n  }\n\n  public List<BasicBlock> getPredecessors() {\n    return predecessors;\n  }\n\n  public List<BasicBlock> getSuccessors() {\n    return successors;\n  }\n\n  public List<BasicBlock> getPredecessorExceptions() {\n    return predecessorExceptions;\n  }\n\n  public List<BasicBlock> getSuccessorExceptions() {\n    return successorExceptions;\n  }\n\n  @Override\n  public List<? extends IGraphNode> getPredecessorNodes() {\n    List<BasicBlock> lst = new ArrayList<>(predecessors);\n    lst.addAll(predecessorExceptions);\n    return lst;\n  }\n\n  @Override\n  public String toString() {\n    return id + \":\" + DecompilerContext.getNewLineSeparator() + seq.toString(0);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.code.cfg;\n\nimport org.jetbrains.java.decompiler.code.*;\nimport org.jetbrains.java.decompiler.code.interpreter.InstructionImpact;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.gen.DataPoint;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.ListStack;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class ControlFlowGraph {\n  public int last_id = 0;\n\n  // *****************************************************************************\n  // private fields\n  // *****************************************************************************\n\n  private VBStyleCollection<BasicBlock, Integer> blocks;\n\n  private BasicBlock first;\n\n  private BasicBlock last;\n\n  private List<ExceptionRangeCFG> exceptions;\n\n  private Map<BasicBlock, BasicBlock> subroutines;\n\n  private final Set<BasicBlock> finallyExits = new HashSet<>();\n\n  // *****************************************************************************\n  // constructors\n  // *****************************************************************************\n\n  public ControlFlowGraph(InstructionSequence seq) {\n    buildBlocks(seq);\n  }\n\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  public void removeMarkers() {\n    for (BasicBlock block : blocks) {\n      block.mark = 0;\n    }\n  }\n\n  public String toString() {\n    if (blocks == null) return \"Empty\";\n\n    String new_line_separator = DecompilerContext.getNewLineSeparator();\n\n    StringBuilder buf = new StringBuilder();\n\n    for (BasicBlock block : blocks) {\n      buf.append(\"----- Block \").append(block.id).append(\" -----\").append(new_line_separator);\n      buf.append(block);\n      buf.append(\"----- Edges -----\").append(new_line_separator);\n\n      List<BasicBlock> suc = block.getSuccessors();\n      for (BasicBlock aSuc : suc) {\n        buf.append(\">>>>>>>>(regular) Block \").append(aSuc.id).append(new_line_separator);\n      }\n      suc = block.getSuccessorExceptions();\n      for (BasicBlock handler : suc) {\n        ExceptionRangeCFG range = getExceptionRange(handler, block);\n\n        if (range == null) {\n          buf.append(\">>>>>>>>(exception) Block \").append(handler.id).append(\"\\t\").append(\"ERROR: range not found!\")\n            .append(new_line_separator);\n        }\n        else {\n          List<String> exceptionTypes = range.getExceptionTypes();\n          if (exceptionTypes == null) {\n            buf.append(\">>>>>>>>(exception) Block \").append(handler.id).append(\"\\t\").append(\"NULL\").append(new_line_separator);\n          }\n          else {\n            for (String exceptionType : exceptionTypes) {\n              buf.append(\">>>>>>>>(exception) Block \").append(handler.id).append(\"\\t\").append(exceptionType).append(new_line_separator);\n            }\n          }\n        }\n      }\n      buf.append(\"----- ----- -----\").append(new_line_separator);\n    }\n\n    return buf.toString();\n  }\n\n  public void inlineJsr(StructClass cl, StructMethod mt) {\n    processJsr();\n    removeJsr(cl, mt);\n\n    removeMarkers();\n\n    DeadCodeHelper.removeEmptyBlocks(this);\n  }\n\n  public void removeBlock(BasicBlock block) {\n\n    while (!block.getSuccessors().isEmpty()) {\n      block.removeSuccessor(block.getSuccessors().get(0));\n    }\n\n    while (!block.getSuccessorExceptions().isEmpty()) {\n      block.removeSuccessorException(block.getSuccessorExceptions().get(0));\n    }\n\n    while (!block.getPredecessors().isEmpty()) {\n      block.getPredecessors().get(0).removeSuccessor(block);\n    }\n\n    while (!block.getPredecessorExceptions().isEmpty()) {\n      block.getPredecessorExceptions().get(0).removeSuccessorException(block);\n    }\n\n    last.removePredecessor(block);\n\n    blocks.removeWithKey(block.id);\n\n    for (int i = exceptions.size() - 1; i >= 0; i--) {\n      ExceptionRangeCFG range = exceptions.get(i);\n      if (range.getHandler() == block) {\n        exceptions.remove(i);\n      }\n      else {\n        List<BasicBlock> lstRange = range.getProtectedRange();\n        lstRange.remove(block);\n\n        if (lstRange.isEmpty()) {\n          exceptions.remove(i);\n        }\n      }\n    }\n\n    subroutines.entrySet().removeIf(ent -> ent.getKey() == block || ent.getValue() == block);\n  }\n\n  public ExceptionRangeCFG getExceptionRange(BasicBlock handler, BasicBlock block) {\n\n    //List<ExceptionRangeCFG> ranges = new ArrayList<ExceptionRangeCFG>();\n\n    for (int i = exceptions.size() - 1; i >= 0; i--) {\n      ExceptionRangeCFG range = exceptions.get(i);\n      if (range.getHandler() == handler && range.getProtectedRange().contains(block)) {\n        return range;\n        //ranges.add(range);\n      }\n    }\n\n    return null;\n    //return ranges.isEmpty() ? null : ranges;\n  }\n\n  //\tpublic String getExceptionsUniqueString(BasicBlock handler, BasicBlock block) {\n  //\n  //\t\tList<ExceptionRangeCFG> ranges = getExceptionRange(handler, block);\n  //\n  //\t\tif(ranges == null) {\n  //\t\t\treturn null;\n  //\t\t} else {\n  //\t\t\tSet<String> setExceptionStrings = new HashSet<String>();\n  //\t\t\tfor(ExceptionRangeCFG range : ranges) {\n  //\t\t\t\tsetExceptionStrings.add(range.getExceptionType());\n  //\t\t\t}\n  //\n  //\t\t\tString ret = \"\";\n  //\t\t\tfor(String exception : setExceptionStrings) {\n  //\t\t\t\tret += exception;\n  //\t\t\t}\n  //\n  //\t\t\treturn ret;\n  //\t\t}\n  //\t}\n\n\n  // *****************************************************************************\n  // private methods\n  // *****************************************************************************\n\n  private void buildBlocks(InstructionSequence instrseq) {\n\n    short[] states = findStartInstructions(instrseq);\n\n    Map<Integer, BasicBlock> mapInstrBlocks = new HashMap<>();\n    VBStyleCollection<BasicBlock, Integer> colBlocks = createBasicBlocks(states, instrseq, mapInstrBlocks);\n\n    blocks = colBlocks;\n\n    connectBlocks(colBlocks, mapInstrBlocks);\n\n    setExceptionEdges(instrseq, mapInstrBlocks);\n\n    setSubroutineEdges();\n\n    setFirstAndLastBlocks();\n  }\n\n  private static short[] findStartInstructions(InstructionSequence seq) {\n\n    int len = seq.length();\n    short[] inststates = new short[len];\n\n    Set<Integer> excSet = new HashSet<>();\n\n    for (ExceptionHandler handler : seq.getExceptionTable().getHandlers()) {\n      excSet.add(handler.from_instr);\n      excSet.add(handler.to_instr);\n      excSet.add(handler.handler_instr);\n    }\n\n\n    for (int i = 0; i < len; i++) {\n\n      // exception blocks\n      if (excSet.contains(i)) {\n        inststates[i] = 1;\n      }\n\n      Instruction instr = seq.getInstr(i);\n      switch (instr.group) {\n        case CodeConstants.GROUP_JUMP:\n          inststates[((JumpInstruction)instr).destination] = 1;\n        case CodeConstants.GROUP_RETURN:\n          if (i + 1 < len) {\n            inststates[i + 1] = 1;\n          }\n          break;\n        case CodeConstants.GROUP_SWITCH:\n          SwitchInstruction swinstr = (SwitchInstruction)instr;\n          int[] dests = swinstr.getDestinations();\n          for (int j = dests.length - 1; j >= 0; j--) {\n            inststates[dests[j]] = 1;\n          }\n          inststates[swinstr.getDefaultDestination()] = 1;\n          if (i + 1 < len) {\n            inststates[i + 1] = 1;\n          }\n      }\n    }\n\n    // first instruction\n    inststates[0] = 1;\n\n    return inststates;\n  }\n\n\n  private VBStyleCollection<BasicBlock, Integer> createBasicBlocks(short[] startblock,\n                                                                   InstructionSequence instrseq,\n                                                                   Map<Integer, BasicBlock> mapInstrBlocks) {\n\n    VBStyleCollection<BasicBlock, Integer> col = new VBStyleCollection<>();\n\n    InstructionSequence currseq = null;\n    List<Integer> lstOffs = null;\n\n    int len = startblock.length;\n    short counter = 0;\n    int blockoffset = 0;\n\n    BasicBlock currentBlock = null;\n    for (int i = 0; i < len; i++) {\n\n      if (startblock[i] == 1) {\n        currentBlock = new BasicBlock(++counter);\n\n        currseq = currentBlock.getSeq();\n        lstOffs = currentBlock.getOriginalOffsets();\n\n        col.addWithKey(currentBlock, currentBlock.id);\n\n        blockoffset = instrseq.getOffset(i);\n      }\n\n      startblock[i] = counter;\n      mapInstrBlocks.put(i, currentBlock);\n\n      currseq.addInstruction(instrseq.getInstr(i), instrseq.getOffset(i) - blockoffset);\n      lstOffs.add(instrseq.getOffset(i));\n    }\n\n    last_id = counter;\n\n    return col;\n  }\n\n\n  private static void connectBlocks(List<BasicBlock> lstbb, Map<Integer, BasicBlock> mapInstrBlocks) {\n\n    for (int i = 0; i < lstbb.size(); i++) {\n\n      BasicBlock block = lstbb.get(i);\n      Instruction instr = block.getLastInstruction();\n\n      boolean fallthrough = instr.canFallThrough();\n      BasicBlock bTemp;\n\n      switch (instr.group) {\n        case CodeConstants.GROUP_JUMP -> {\n          int dest = ((JumpInstruction)instr).destination;\n          bTemp = mapInstrBlocks.get(dest);\n          block.addSuccessor(bTemp);\n        }\n        case CodeConstants.GROUP_SWITCH -> {\n          SwitchInstruction sinstr = (SwitchInstruction)instr;\n          int[] dests = sinstr.getDestinations();\n\n          bTemp = mapInstrBlocks.get(((SwitchInstruction)instr).getDefaultDestination());\n          block.addSuccessor(bTemp);\n          for (int dest1 : dests) {\n            bTemp = mapInstrBlocks.get(dest1);\n            block.addSuccessor(bTemp);\n          }\n        }\n      }\n\n      if (fallthrough && i < lstbb.size() - 1) {\n        BasicBlock defaultBlock = lstbb.get(i + 1);\n        block.addSuccessor(defaultBlock);\n      }\n    }\n  }\n\n  private void setExceptionEdges(InstructionSequence instrseq, Map<Integer, BasicBlock> instrBlocks) {\n\n    exceptions = new ArrayList<>();\n\n    Map<String, ExceptionRangeCFG> mapRanges = new HashMap<>();\n\n    for (ExceptionHandler handler : instrseq.getExceptionTable().getHandlers()) {\n\n      BasicBlock from = instrBlocks.get(handler.from_instr);\n      BasicBlock to = instrBlocks.get(handler.to_instr);\n      BasicBlock handle = instrBlocks.get(handler.handler_instr);\n\n      String key = from.id + \":\" + to.id + \":\" + handle.id;\n\n      if (mapRanges.containsKey(key)) {\n        ExceptionRangeCFG range = mapRanges.get(key);\n        range.addExceptionType(handler.exceptionClass);\n      }\n      else {\n\n        List<BasicBlock> protectedRange = new ArrayList<>();\n        for (int j = from.id; j < to.id; j++) {\n          BasicBlock block = blocks.getWithKey(j);\n          protectedRange.add(block);\n          block.addSuccessorException(handle);\n        }\n\n        ExceptionRangeCFG range = new ExceptionRangeCFG(protectedRange, handle, handler.exceptionClass == null\n                                                                                ? null\n                                                                                : Collections.singletonList(handler.exceptionClass));\n        mapRanges.put(key, range);\n\n        exceptions.add(range);\n      }\n    }\n  }\n\n  private void setSubroutineEdges() {\n    final Map<BasicBlock, BasicBlock> subroutines = new LinkedHashMap<>();\n\n    for (BasicBlock block : blocks) {\n\n      if (block.getSeq().getLastInstr().opcode == CodeConstants.opc_jsr) {\n\n        LinkedList<BasicBlock> stack = new LinkedList<>();\n        LinkedList<LinkedList<BasicBlock>> stackJsrStacks = new LinkedList<>();\n\n        Set<BasicBlock> setVisited = new HashSet<>();\n\n        stack.add(block);\n        stackJsrStacks.add(new LinkedList<>());\n\n        while (!stack.isEmpty()) {\n\n          BasicBlock node = stack.removeFirst();\n          LinkedList<BasicBlock> jsrstack = stackJsrStacks.removeFirst();\n\n          setVisited.add(node);\n\n          switch (node.getSeq().getLastInstr().opcode) {\n            case CodeConstants.opc_jsr -> jsrstack.add(node);\n            case CodeConstants.opc_ret -> {\n              BasicBlock enter = jsrstack.getLast();\n              BasicBlock exit = blocks.getWithKey(enter.id + 1); // FIXME: find successor in a better way\n\n              if (exit != null) {\n                if (!node.isSuccessor(exit)) {\n                  node.addSuccessor(exit);\n                }\n                jsrstack.removeLast();\n                subroutines.put(enter, exit);\n              }\n              else {\n                throw new RuntimeException(\"ERROR: last instruction jsr\");\n              }\n            }\n          }\n\n          if (!jsrstack.isEmpty()) {\n            for (BasicBlock succ : node.getSuccessors()) {\n              if (!setVisited.contains(succ)) {\n                stack.add(succ);\n                stackJsrStacks.add(new LinkedList<>(jsrstack));\n              }\n            }\n          }\n        }\n      }\n    }\n\n    this.subroutines = subroutines;\n  }\n\n  private void processJsr() {\n    while (true) {\n      if (processJsrRanges() == 0) break;\n    }\n  }\n\n  private static final class JsrRecord {\n    private final BasicBlock jsr;\n    private final Set<BasicBlock> range;\n    private final BasicBlock ret;\n\n    private JsrRecord(BasicBlock jsr, Set<BasicBlock> range, BasicBlock ret) {\n      this.jsr = jsr;\n      this.range = range;\n      this.ret = ret;\n    }\n  }\n\n  private int processJsrRanges() {\n\n    List<JsrRecord> lstJsrAll = new ArrayList<>();\n\n    // get all jsr ranges\n    for (Entry<BasicBlock, BasicBlock> ent : subroutines.entrySet()) {\n      BasicBlock jsr = ent.getKey();\n      BasicBlock ret = ent.getValue();\n\n      lstJsrAll.add(new JsrRecord(jsr, getJsrRange(jsr, ret), ret));\n    }\n\n    // sort ranges\n    // FIXME: better sort order\n    List<JsrRecord> lstJsr = new ArrayList<>();\n    for (JsrRecord arr : lstJsrAll) {\n      int i = 0;\n      for (; i < lstJsr.size(); i++) {\n        JsrRecord arrJsr = lstJsr.get(i);\n        if (arrJsr.range.contains(arr.jsr)) {\n          break;\n        }\n      }\n      lstJsr.add(i, arr);\n    }\n\n    // find the first intersection\n    for (int i = 0; i < lstJsr.size(); i++) {\n      JsrRecord arr = lstJsr.get(i);\n      Set<BasicBlock> set = arr.range;\n\n      for (int j = i + 1; j < lstJsr.size(); j++) {\n        JsrRecord arr1 = lstJsr.get(j);\n        Set<BasicBlock> set1 = arr1.range;\n\n        if (!set.contains(arr1.jsr) && !set1.contains(arr.jsr)) { // rang 0 doesn't contain entry 1 and vice versa\n          Set<BasicBlock> setc = new HashSet<>(set);\n          setc.retainAll(set1);\n\n          if (!setc.isEmpty()) {\n            splitJsrRange(arr.jsr, arr.ret, setc);\n            return 1;\n          }\n        }\n      }\n    }\n\n    return 0;\n  }\n\n  private Set<BasicBlock> getJsrRange(BasicBlock jsr, BasicBlock ret) {\n\n    Set<BasicBlock> blocks = new HashSet<>();\n\n    List<BasicBlock> lstNodes = new LinkedList<>();\n    lstNodes.add(jsr);\n\n    BasicBlock dom = jsr.getSuccessors().get(0);\n\n    while (!lstNodes.isEmpty()) {\n\n      BasicBlock node = lstNodes.remove(0);\n\n      for (int j = 0; j < 2; j++) {\n        List<BasicBlock> lst;\n        if (j == 0) {\n          if (node.getLastInstruction().opcode == CodeConstants.opc_ret) {\n            if (node.getSuccessors().contains(ret)) {\n              continue;\n            }\n          }\n          lst = node.getSuccessors();\n        }\n        else {\n          if (node == jsr) {\n            continue;\n          }\n          lst = node.getSuccessorExceptions();\n        }\n\n        CHILD:\n        for (int i = lst.size() - 1; i >= 0; i--) {\n\n          BasicBlock child = lst.get(i);\n          if (!blocks.contains(child)) {\n\n            if (node != jsr) {\n              for (int k = 0; k < child.getPredecessors().size(); k++) {\n                if (!DeadCodeHelper.isDominator(this, child.getPredecessors().get(k), dom)) {\n                  continue CHILD;\n                }\n              }\n\n              for (int k = 0; k < child.getPredecessorExceptions().size(); k++) {\n                if (!DeadCodeHelper.isDominator(this, child.getPredecessorExceptions().get(k), dom)) {\n                  continue CHILD;\n                }\n              }\n            }\n\n            // last block is a dummy one\n            if (child != last) {\n              blocks.add(child);\n            }\n\n            lstNodes.add(child);\n          }\n        }\n      }\n    }\n\n    return blocks;\n  }\n\n  private void splitJsrRange(BasicBlock jsr, BasicBlock ret, Set<BasicBlock> common_blocks) {\n\n    List<BasicBlock> lstNodes = new LinkedList<>();\n    Map<Integer, BasicBlock> mapNewNodes = new HashMap<>();\n\n    lstNodes.add(jsr);\n    mapNewNodes.put(jsr.id, jsr);\n\n    while (!lstNodes.isEmpty()) {\n\n      BasicBlock node = lstNodes.remove(0);\n\n      for (int j = 0; j < 2; j++) {\n        List<BasicBlock> lst;\n        if (j == 0) {\n          if (node.getLastInstruction().opcode == CodeConstants.opc_ret) {\n            if (node.getSuccessors().contains(ret)) {\n              continue;\n            }\n          }\n          lst = node.getSuccessors();\n        }\n        else {\n          if (node == jsr) {\n            continue;\n          }\n          lst = node.getSuccessorExceptions();\n        }\n\n\n        for (int i = lst.size() - 1; i >= 0; i--) {\n\n          BasicBlock child = lst.get(i);\n          Integer childid = child.id;\n\n          if (mapNewNodes.containsKey(childid)) {\n            node.replaceSuccessor(child, mapNewNodes.get(childid));\n          }\n          else if (common_blocks.contains(child)) {\n            // make a copy of the current block\n            BasicBlock copy = child.clone(++last_id);\n            // copy all successors\n            if (copy.getLastInstruction().opcode == CodeConstants.opc_ret && child.getSuccessors().contains(ret)) {\n              copy.addSuccessor(ret);\n              child.removeSuccessor(ret);\n            }\n            else {\n              for (int k = 0; k < child.getSuccessors().size(); k++) {\n                copy.addSuccessor(child.getSuccessors().get(k));\n              }\n            }\n            for (int k = 0; k < child.getSuccessorExceptions().size(); k++) {\n              copy.addSuccessorException(child.getSuccessorExceptions().get(k));\n            }\n\n            lstNodes.add(copy);\n            mapNewNodes.put(childid, copy);\n\n            if (last.getPredecessors().contains(child)) {\n              last.addPredecessor(copy);\n            }\n\n            node.replaceSuccessor(child, copy);\n            blocks.addWithKey(copy, copy.id);\n          }\n          else {\n            // stop at the first fixed node\n            //lstNodes.add(child);\n            mapNewNodes.put(childid, child);\n          }\n        }\n      }\n    }\n\n    // note: subroutines won't be copied!\n    splitJsrExceptionRanges(common_blocks, mapNewNodes);\n  }\n\n  private void splitJsrExceptionRanges(Set<BasicBlock> common_blocks, Map<Integer, BasicBlock> mapNewNodes) {\n\n    for (int i = exceptions.size() - 1; i >= 0; i--) {\n\n      ExceptionRangeCFG range = exceptions.get(i);\n      List<BasicBlock> lstRange = range.getProtectedRange();\n\n      HashSet<BasicBlock> setBoth = new HashSet<>(common_blocks);\n      setBoth.retainAll(lstRange);\n\n      if (!setBoth.isEmpty()) {\n        List<BasicBlock> lstNewRange;\n\n        if (setBoth.size() == lstRange.size()) {\n          lstNewRange = new ArrayList<>();\n          ExceptionRangeCFG newRange = new ExceptionRangeCFG(lstNewRange,\n                                                             mapNewNodes.get(range.getHandler().id), range.getExceptionTypes());\n          exceptions.add(newRange);\n        }\n        else {\n          lstNewRange = lstRange;\n        }\n\n        for (BasicBlock block : setBoth) {\n          lstNewRange.add(mapNewNodes.get(block.id));\n        }\n      }\n    }\n  }\n\n  private void removeJsr(StructClass cl, StructMethod mt) {\n    removeJsrInstructions(cl.getPool(), first, DataPoint.getInitialDataPoint(mt));\n  }\n\n  private static void removeJsrInstructions(ConstantPool pool, BasicBlock block, DataPoint data) {\n    ListStack<VarType> stack = data.getStack();\n\n    InstructionSequence seq = block.getSeq();\n    for (int i = 0; i < seq.length(); i++) {\n      Instruction instr = seq.getInstr(i);\n\n      VarType var = null;\n      if (instr.opcode == CodeConstants.opc_astore || instr.opcode == CodeConstants.opc_pop) {\n        var = stack.getByOffset(-1);\n      }\n\n      InstructionImpact.stepTypes(data, instr, pool);\n\n      switch (instr.opcode) {\n        case CodeConstants.opc_jsr, CodeConstants.opc_ret -> {\n          seq.removeInstruction(i);\n          i--;\n        }\n        case CodeConstants.opc_astore, CodeConstants.opc_pop -> {\n          if (var.getType() == CodeConstants.TYPE_ADDRESS) {\n            seq.removeInstruction(i);\n            i--;\n          }\n        }\n      }\n    }\n\n    block.mark = 1;\n\n    for (int i = 0; i < block.getSuccessors().size(); i++) {\n      BasicBlock suc = block.getSuccessors().get(i);\n      if (suc.mark != 1) {\n        removeJsrInstructions(pool, suc, data.copy());\n      }\n    }\n\n    for (int i = 0; i < block.getSuccessorExceptions().size(); i++) {\n      BasicBlock suc = block.getSuccessorExceptions().get(i);\n      if (suc.mark != 1) {\n        DataPoint point = data.copy();\n        point.getStack().clear();\n        point.getStack().push(new VarType(CodeConstants.TYPE_OBJECT, 0, null));\n        removeJsrInstructions(pool, suc, point);\n      }\n    }\n  }\n\n  private void setFirstAndLastBlocks() {\n\n    first = blocks.get(0);\n\n    last = new BasicBlock(++last_id);\n\n    for (BasicBlock block : blocks) {\n      if (block.getSuccessors().isEmpty()) {\n        last.addPredecessor(block);\n      }\n    }\n  }\n\n  public List<BasicBlock> getReversePostOrder() {\n\n    List<BasicBlock> res = new LinkedList<>();\n    addToReversePostOrderListIterative(first, res);\n\n    return res;\n  }\n\n  private static void addToReversePostOrderListIterative(BasicBlock root, List<? super BasicBlock> lst) {\n\n    LinkedList<BasicBlock> stackNode = new LinkedList<>();\n    LinkedList<Integer> stackIndex = new LinkedList<>();\n\n    Set<BasicBlock> setVisited = new HashSet<>();\n\n    stackNode.add(root);\n    stackIndex.add(0);\n\n    while (!stackNode.isEmpty()) {\n\n      BasicBlock node = stackNode.getLast();\n      int index = stackIndex.removeLast();\n\n      setVisited.add(node);\n\n      List<BasicBlock> lstSuccs = new ArrayList<>(node.getSuccessors());\n      lstSuccs.addAll(node.getSuccessorExceptions());\n\n      for (; index < lstSuccs.size(); index++) {\n        BasicBlock succ = lstSuccs.get(index);\n\n        if (!setVisited.contains(succ)) {\n          stackIndex.add(index + 1);\n\n          stackNode.add(succ);\n          stackIndex.add(0);\n\n          break;\n        }\n      }\n\n      if (index == lstSuccs.size()) {\n        lst.add(0, node);\n\n        stackNode.removeLast();\n      }\n    }\n  }\n\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public VBStyleCollection<BasicBlock, Integer> getBlocks() {\n    return blocks;\n  }\n\n  public BasicBlock getFirst() {\n    return first;\n  }\n\n  public void setFirst(BasicBlock first) {\n    this.first = first;\n  }\n\n  public List<ExceptionRangeCFG> getExceptions() {\n    return exceptions;\n  }\n\n  public BasicBlock getLast() {\n    return last;\n  }\n\n  public Set<BasicBlock> getFinallyExits() {\n    return finallyExits;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.code.cfg;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class ExceptionRangeCFG {\n  private final List<BasicBlock> protectedRange; // FIXME: replace with set\n  private BasicBlock handler;\n  private List<String> exceptionTypes;\n\n  public ExceptionRangeCFG(List<BasicBlock> protectedRange, BasicBlock handler, List<String> exceptionType) {\n    this.protectedRange = protectedRange;\n    this.handler = handler;\n\n    if (exceptionType != null) {\n      this.exceptionTypes = new ArrayList<>(exceptionType);\n    }\n  }\n\n  public boolean isCircular() {\n    return protectedRange.contains(handler);\n  }\n\n  @Override\n  public String toString() {\n    String new_line_separator = DecompilerContext.getNewLineSeparator();\n    StringBuilder buf = new StringBuilder();\n\n    buf.append(\"exceptionType:\");\n\n    if (exceptionTypes == null) {\n      buf.append(\" null\");\n    }\n    else {\n      for (String exception_type : exceptionTypes) {\n        buf.append(\" \").append(exception_type);\n      }\n    }\n\n    buf.append(new_line_separator);\n\n    buf.append(\"handler: \").append(handler.id).append(new_line_separator);\n    buf.append(\"range: \");\n    for (BasicBlock block : protectedRange) {\n      buf.append(block.id).append(\" \");\n    }\n    buf.append(new_line_separator);\n\n    return buf.toString();\n  }\n\n  public BasicBlock getHandler() {\n    return handler;\n  }\n\n  public void setHandler(BasicBlock handler) {\n    this.handler = handler;\n  }\n\n  public List<BasicBlock> getProtectedRange() {\n    return protectedRange;\n  }\n\n  public List<String> getExceptionTypes() {\n    return this.exceptionTypes;\n  }\n\n  public void addExceptionType(String exceptionType) {\n    if (this.exceptionTypes == null) {\n      return;\n    }\n\n    if (exceptionType == null) {\n      this.exceptionTypes = null;\n    }\n    else {\n      this.exceptionTypes.add(exceptionType);\n    }\n  }\n\n  public String getUniqueExceptionsString() {\n    return exceptionTypes != null ? exceptionTypes.stream().distinct().collect(Collectors.joining(\":\")) : null;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.code.interpreter;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.consts.LinkConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PooledConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.DataPoint;\nimport org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.ListStack;\n\npublic final class InstructionImpact {\n\n  // {read, write}\n  private static final int[][][] stack_impact = {\n\n    {null, null},                                                //\t\tpublic final static int\t\topc_nop = 0;\n    null,                                                                //\t\tpublic final static int\t\topc_aconst_null = 1;\n    null,                                                                //\t\tpublic final static int\t\topc_iconst_m1 = 2;\n    null,                                                                //\t\tpublic final static int\t\topc_iconst_0 = 3;\n    null,                                                                //\t\tpublic final static int\t\topc_iconst_1 = 4;\n    null,                                                                //\t\tpublic final static int\t\topc_iconst_2 = 5;\n    null,                        //\t\tpublic final static int\t\topc_iconst_3 = 6;\n    null,                        //\t\tpublic final static int\t\topc_iconst_4 = 7;\n    null,                        //\t\tpublic final static int\t\topc_iconst_5 = 8;\n    {null, {CodeConstants.TYPE_LONG}},                        //\t\tpublic final static int\t\topc_lconst_0 = 9;\n    {null, {CodeConstants.TYPE_LONG}},                        //\t\tpublic final static int\t\topc_lconst_1 = 10;\n    {null, {CodeConstants.TYPE_FLOAT}},                        //\t\tpublic final static int\t\topc_fconst_0 = 11;\n    {null, {CodeConstants.TYPE_FLOAT}},                        //\t\tpublic final static int\t\topc_fconst_1 = 12;\n    {null, {CodeConstants.TYPE_FLOAT}},                        //\t\tpublic final static int\t\topc_fconst_2 = 13;\n    {null, {CodeConstants.TYPE_DOUBLE}},                        //\t\tpublic final static int\t\topc_dconst_0 = 14;\n    {null, {CodeConstants.TYPE_DOUBLE}},                        //\t\tpublic final static int\t\topc_dconst_1 = 15;\n    {null, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_bipush = 16;\n    {null, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_sipush = 17;\n    null,                        //\t\tpublic final static int\t\topc_ldc = 18;\n    null,                        //\t\tpublic final static int\t\topc_ldc_w = 19;\n    null,                        //\t\tpublic final static int\t\topc_ldc2_w = 20;\n    {null, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_iload = 21;\n    {null, {CodeConstants.TYPE_LONG}},                        //\t\tpublic final static int\t\topc_lload = 22;\n    {null, {CodeConstants.TYPE_FLOAT}},                        //\t\tpublic final static int\t\topc_fload = 23;\n    {null, {CodeConstants.TYPE_DOUBLE}},                        //\t\tpublic final static int\t\topc_dload = 24;\n    null,                        //\t\tpublic final static int\t\topc_aload = 25;\n    null,                        //\t\tpublic final static int\t\topc_iload_0 = 26;\n    null,                        //\t\tpublic final static int\t\topc_iload_1 = 27;\n    null,                        //\t\tpublic final static int\t\topc_iload_2 = 28;\n    null,                        //\t\tpublic final static int\t\topc_iload_3 = 29;\n    null,                        //\t\tpublic final static int\t\topc_lload_0 = 30;\n    null,                        //\t\tpublic final static int\t\topc_lload_1 = 31;\n    null,                        //\t\tpublic final static int\t\topc_lload_2 = 32;\n    null,                        //\t\tpublic final static int\t\topc_lload_3 = 33;\n    null,                        //\t\tpublic final static int\t\topc_fload_0 = 34;\n    null,                        //\t\tpublic final static int\t\topc_fload_1 = 35;\n    null,                        //\t\tpublic final static int\t\topc_fload_2 = 36;\n    null,                        //\t\tpublic final static int\t\topc_fload_3 = 37;\n    null,                        //\t\tpublic final static int\t\topc_dload_0 = 38;\n    null,                        //\t\tpublic final static int\t\topc_dload_1 = 39;\n    null,                        //\t\tpublic final static int\t\topc_dload_2 = 40;\n    null,                        //\t\tpublic final static int\t\topc_dload_3 = 41;\n    null,                        //\t\tpublic final static int\t\topc_aload_0 = 42;\n    null,                        //\t\tpublic final static int\t\topc_aload_1 = 43;\n    null,                        //\t\tpublic final static int\t\topc_aload_2 = 44;\n    null,                        //\t\tpublic final static int\t\topc_aload_3 = 45;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_iaload = 46;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_laload = 47;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_FLOAT}},\n    //\t\tpublic final static int\t\topc_faload = 48;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_DOUBLE}},\n    //\t\tpublic final static int\t\topc_daload = 49;\n    null,                        //\t\tpublic final static int\t\topc_aaload = 50;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_baload = 51;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_caload = 52;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_saload = 53;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_istore = 54;\n    {{CodeConstants.TYPE_LONG}, null},                        //\t\tpublic final static int\t\topc_lstore = 55;\n    {{CodeConstants.TYPE_FLOAT}, null},                        //\t\tpublic final static int\t\topc_fstore = 56;\n    {{CodeConstants.TYPE_DOUBLE}, null},                        //\t\tpublic final static int\t\topc_dstore = 57;\n    null,                        //\t\tpublic final static int\t\topc_astore = 58;\n    null,                        //\t\tpublic final static int\t\topc_istore_0 = 59;\n    null,                        //\t\tpublic final static int\t\topc_istore_1 = 60;\n    null,                        //\t\tpublic final static int\t\topc_istore_2 = 61;\n    null,                        //\t\tpublic final static int\t\topc_istore_3 = 62;\n    null,                        //\t\tpublic final static int\t\topc_lstore_0 = 63;\n    null,                        //\t\tpublic final static int\t\topc_lstore_1 = 64;\n    null,                        //\t\tpublic final static int\t\topc_lstore_2 = 65;\n    null,                        //\t\tpublic final static int\t\topc_lstore_3 = 66;\n    null,                        //\t\tpublic final static int\t\topc_fstore_0 = 67;\n    null,                        //\t\tpublic final static int\t\topc_fstore_1 = 68;\n    null,                        //\t\tpublic final static int\t\topc_fstore_2 = 69;\n    null,                        //\t\tpublic final static int\t\topc_fstore_3 = 70;\n    null,                        //\t\tpublic final static int\t\topc_dstore_0 = 71;\n    null,                        //\t\tpublic final static int\t\topc_dstore_1 = 72;\n    null,                        //\t\tpublic final static int\t\topc_dstore_2 = 73;\n    null,                        //\t\tpublic final static int\t\topc_dstore_3 = 74;\n    null,                        //\t\tpublic final static int\t\topc_astore_0 = 75;\n    null,                        //\t\tpublic final static int\t\topc_astore_1 = 76;\n    null,                        //\t\tpublic final static int\t\topc_astore_2 = 77;\n    null,                        //\t\tpublic final static int\t\topc_astore_3 = 78;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},\n    //\t\tpublic final static int\t\topc_iastore = 79;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG}, null},\n    //\t\tpublic final static int\t\topc_lastore = 80;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_FLOAT}, null},\n    //\t\tpublic final static int\t\topc_fastore = 81;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_DOUBLE}, null},\n    //\t\tpublic final static int\t\topc_dastore = 82;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_OBJECT}, null},\n    //\t\tpublic final static int\t\topc_aastore = 83;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},\n    //\t\tpublic final static int\t\topc_bastore = 84;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},\n    //\t\tpublic final static int\t\topc_castore = 85;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},\n    //\t\tpublic final static int\t\topc_sastore = 86;\n    {{CodeConstants.TYPE_ANY}, null},                        //\t\tpublic final static int\t\topc_pop = 87;\n    {{CodeConstants.TYPE_ANY, CodeConstants.TYPE_ANY}, null},                        //\t\tpublic final static int\t\topc_pop2 = 88;\n    null,                        //\t\tpublic final static int\t\topc_dup = 89;\n    null,                        //\t\tpublic final static int\t\topc_dup_x1 = 90;\n    null,                        //\t\tpublic final static int\t\topc_dup_x2 = 91;\n    null,                        //\t\tpublic final static int\t\topc_dup2 = 92;\n    null,                        //\t\tpublic final static int\t\topc_dup2_x1 = 93;\n    null,                        //\t\tpublic final static int\t\topc_dup2_x2 = 94;\n    null,                        //\t\tpublic final static int\t\topc_swap = 95;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_iadd = 96;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_ladd = 97;\n    {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}},\n    //\t\tpublic final static int\t\topc_fadd = 98;\n    {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}},\n    //\t\tpublic final static int\t\topc_dadd = 99;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_isub = 100;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_lsub = 101;\n    {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}},\n    //\t\tpublic final static int\t\topc_fsub = 102;\n    {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}},\n    //\t\tpublic final static int\t\topc_dsub = 103;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_imul = 104;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_lmul = 105;\n    {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}},\n    //\t\tpublic final static int\t\topc_fmul = 106;\n    {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}},\n    //\t\tpublic final static int\t\topc_dmul = 107;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_idiv = 108;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_ldiv = 109;\n    {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}},\n    //\t\tpublic final static int\t\topc_fdiv = 110;\n    {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}},\n    //\t\tpublic final static int\t\topc_ddiv = 111;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_irem = 112;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_lrem = 113;\n    {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}},\n    //\t\tpublic final static int\t\topc_frem = 114;\n    {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}},\n    //\t\tpublic final static int\t\topc_drem = 115;\n    {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_ineg = 116;\n    {{CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},                        //\t\tpublic final static int\t\topc_lneg = 117;\n    {{CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_FLOAT}},                        //\t\tpublic final static int\t\topc_fneg = 118;\n    {{CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_DOUBLE}},                        //\t\tpublic final static int\t\topc_dneg = 119;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_ishl = 120;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_lshl = 121;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_ishr = 122;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_lshr = 123;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_iushr = 124;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_lushr = 125;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_iand = 126;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_land = 127;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_ior = 128;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_lor = 129;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_ixor = 130;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_LONG}},\n    //\t\tpublic final static int\t\topc_lxor = 131;\n    {null, null},                        //\t\tpublic final static int\t\topc_iinc = 132;\n    {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_LONG}},                        //\t\tpublic final static int\t\topc_i2l = 133;\n    {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_FLOAT}},                        //\t\tpublic final static int\t\topc_i2f = 134;\n    {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_DOUBLE}},                        //\t\tpublic final static int\t\topc_i2d = 135;\n    {{CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_l2i = 136;\n    {{CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_FLOAT}},                        //\t\tpublic final static int\t\topc_l2f = 137;\n    {{CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_DOUBLE}},                        //\t\tpublic final static int\t\topc_l2d = 138;\n    {{CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_f2i = 139;\n    {{CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_LONG}},                        //\t\tpublic final static int\t\topc_f2l = 140;\n    {{CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_DOUBLE}},                        //\t\tpublic final static int\t\topc_f2d = 141;\n    {{CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_d2i = 142;\n    {{CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_LONG}},                        //\t\tpublic final static int\t\topc_d2l = 143;\n    {{CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_FLOAT}},                        //\t\tpublic final static int\t\topc_d2f = 144;\n    {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_i2b = 145;\n    {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_i2c = 146;\n    {{CodeConstants.TYPE_INT}, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_i2s = 147;\n    {{CodeConstants.TYPE_LONG, CodeConstants.TYPE_LONG}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_lcmp = 148;\n    {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_fcmpl = 149;\n    {{CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_FLOAT}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_fcmpg = 150;\n    {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_dcmpl = 151;\n    {{CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_DOUBLE}, {CodeConstants.TYPE_INT}},\n    //\t\tpublic final static int\t\topc_dcmpg = 152;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_ifeq = 153;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_ifne = 154;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_iflt = 155;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_ifge = 156;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_ifgt = 157;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_ifle = 158;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_if_icmpeq = 159;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_if_icmpne = 160;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_if_icmplt = 161;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_if_icmpge = 162;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_if_icmpgt = 163;\n    {{CodeConstants.TYPE_INT, CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_if_icmple = 164;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_OBJECT}, null},\n    //\t\tpublic final static int\t\topc_if_acmpeq = 165;\n    {{CodeConstants.TYPE_OBJECT, CodeConstants.TYPE_OBJECT}, null},\n    //\t\tpublic final static int\t\topc_if_acmpne = 166;\n    {null, null},                        //\t\tpublic final static int\t\topc_goto = 167;\n    {null, {CodeConstants.TYPE_ADDRESS}},                        //\t\tpublic final static int\t\topc_jsr = 168;\n    {null, null},                        //\t\tpublic final static int\t\topc_ret = 169;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_tableswitch = 170;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_lookupswitch = 171;\n    {{CodeConstants.TYPE_INT}, null},                        //\t\tpublic final static int\t\topc_ireturn = 172;\n    {{CodeConstants.TYPE_LONG}, null},                        //\t\tpublic final static int\t\topc_lreturn = 173;\n    {{CodeConstants.TYPE_FLOAT}, null},                        //\t\tpublic final static int\t\topc_freturn = 174;\n    {{CodeConstants.TYPE_DOUBLE}, null},                        //\t\tpublic final static int\t\topc_dreturn = 175;\n    {{CodeConstants.TYPE_OBJECT}, null},                        //\t\tpublic final static int\t\topc_areturn = 176;\n    {null, null},                        //\t\tpublic final static int\t\topc_return = 177;\n    null,                        //\t\tpublic final static int\t\topc_getstatic = 178;\n    null,                        //\t\tpublic final static int\t\topc_putstatic = 179;\n    null,                        //\t\tpublic final static int\t\topc_getfield = 180;\n    null,                        //\t\tpublic final static int\t\topc_putfield = 181;\n    null,                        //\t\tpublic final static int\t\topc_invokevirtual = 182;\n    null,                        //\t\tpublic final static int\t\topc_invokespecial = 183;\n    null,                        //\t\tpublic final static int\t\topc_invokestatic = 184;\n    null,                        //\t\tpublic final static int\t\topc_invokeinterface = 185;\n    null,                        //\t\tpublic final static int\t\topc_xxxunusedxxx = 186;\n    null,                        //\t\tpublic final static int\t\topc_new = 187;\n    null,                        //\t\tpublic final static int\t\topc_newarray = 188;\n    null,                        //\t\tpublic final static int\t\topc_anewarray = 189;\n    {{CodeConstants.TYPE_OBJECT}, {CodeConstants.TYPE_INT}},                        //\t\tpublic final static int\t\topc_arraylength = 190;\n    null,\n    //\t\tpublic final static int\t\topc_athrow = 191;\n    null,\n    //\t\tpublic final static int\t\topc_checkcast = 192;\n    null,\n    //\t\tpublic final static int\t\topc_instanceof = 193;\n    {{CodeConstants.TYPE_OBJECT}, null},                                //\t\tpublic final static int\t\topc_monitorenter = 194;\n    {{CodeConstants.TYPE_OBJECT}, null},                                //\t\tpublic final static int\t\topc_monitorexit = 195;\n    null,\n    //\t\tpublic final static int\t\topc_wide = 196;\n    null,\n    //\t\tpublic final static int\t\topc_multianewarray = 197;\n    {{CodeConstants.TYPE_OBJECT}, null},                                //\t\tpublic final static int\t\topc_ifnull = 198;\n    {{CodeConstants.TYPE_OBJECT}, null},                                //\t\tpublic final static int\t\topc_ifnonnull = 199;\n    {null, null},                                                                        //\t\tpublic final static int\t\topc_goto_w = 200;\n    {null, {CodeConstants.TYPE_ADDRESS}},                        //\t\tpublic final static int\t\topc_jsr_w = 201;\n  };\n\n  private static final int[] arr_type = new int[]{\n    CodeConstants.TYPE_BOOLEAN,\n    CodeConstants.TYPE_CHAR,\n    CodeConstants.TYPE_FLOAT,\n    CodeConstants.TYPE_DOUBLE,\n    CodeConstants.TYPE_BYTE,\n    CodeConstants.TYPE_SHORT,\n    CodeConstants.TYPE_INT,\n    CodeConstants.TYPE_LONG\n  };\n\n\n  // Sonderbehandlung\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_aconst_null = 1;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_ldc = 18;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_ldc_w = 19;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_ldc2_w = 20;\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_aload = 25;\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_aaload = 50;\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_astore = 58;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_dup = 89;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_dup_x1 = 90;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_dup_x2 = 91;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_dup2 = 92;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_dup2_x1 = 93;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_dup2_x2 = 94;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_swap = 95;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_getstatic = 178;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_putstatic = 179;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_getfield = 180;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_putfield = 181;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_invokevirtual = 182;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_invokespecial = 183;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_invokestatic = 184;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_invokeinterface = 185;\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_new = 187;\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_newarray = 188;\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_anewarray = 189;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_athrow = 191;\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_checkcast = 192;\n  //\tnull,\t\t\t//\t\tpublic final static int\t\topc_instanceof = 193;\n  //\tnull, \t\t\t//\t\tpublic final static int\t\topc_multianewarray = 197;\n\n\n  public static void stepTypes(DataPoint data, Instruction instr, ConstantPool pool) {\n    ListStack<VarType> stack = data.getStack();\n    int[][] arr = stack_impact[instr.opcode];\n\n    if (arr != null) {\n      // simple types only\n\n      int[] read = arr[0];\n      int[] write = arr[1];\n\n      if (read != null) {\n        int depth = 0;\n        for (int type : read) {\n          depth++;\n          if (type == CodeConstants.TYPE_LONG ||\n              type == CodeConstants.TYPE_DOUBLE) {\n            depth++;\n          }\n        }\n\n        stack.removeMultiple(depth);\n      }\n\n      if (write != null) {\n        for (int type : write) {\n          stack.push(new VarType(type));\n          if (type == CodeConstants.TYPE_LONG ||\n              type == CodeConstants.TYPE_DOUBLE) {\n            stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));\n          }\n        }\n      }\n    }\n    else {\n      // Sonderbehandlung\n      processSpecialInstructions(data, instr, pool);\n    }\n  }\n\n  private static void processSpecialInstructions(DataPoint data, Instruction instr, ConstantPool pool) {\n\n    VarType var1;\n    PrimitiveConstant cn;\n    LinkConstant ck;\n\n    ListStack<VarType> stack = data.getStack();\n\n    switch (instr.opcode) {\n      case CodeConstants.opc_aconst_null:\n        stack.push(new VarType(CodeConstants.TYPE_NULL, 0, null));\n        break;\n      case CodeConstants.opc_ldc:\n      case CodeConstants.opc_ldc_w:\n      case CodeConstants.opc_ldc2_w:\n        PooledConstant constant = pool.getConstant(instr.operand(0));\n        switch (constant.type) {\n          case CodeConstants.CONSTANT_Integer -> stack.push(new VarType(CodeConstants.TYPE_INT));\n          case CodeConstants.CONSTANT_Float -> stack.push(new VarType(CodeConstants.TYPE_FLOAT));\n          case CodeConstants.CONSTANT_Long -> {\n            stack.push(new VarType(CodeConstants.TYPE_LONG));\n            stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));\n          }\n          case CodeConstants.CONSTANT_Double -> {\n            stack.push(new VarType(CodeConstants.TYPE_DOUBLE));\n            stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));\n          }\n          case CodeConstants.CONSTANT_String -> stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/String\"));\n          case CodeConstants.CONSTANT_Class -> stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Class\"));\n          case CodeConstants.CONSTANT_MethodHandle -> stack.push(new VarType(((LinkConstant)constant).descriptor));\n          case CodeConstants.CONSTANT_Dynamic -> {\n            ck = pool.getLinkConstant(instr.operand(0));\n            FieldDescriptor constDescriptor = FieldDescriptor.parseDescriptor(ck.descriptor);\n            stack.push(constDescriptor.type);\n            if (constDescriptor.type.getStackSize() == 2) {\n              stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));\n            }\n          }\n        }\n        break;\n      case CodeConstants.opc_aload:\n        var1 = data.getVariable(instr.operand(0));\n        if (var1 != null) {\n          stack.push(var1);\n        }\n        else {\n          stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, null));\n        }\n        break;\n      case CodeConstants.opc_aaload:\n        var1 = stack.pop(2);\n        stack.push(new VarType(var1.getType(), var1.getArrayDim() - 1, var1.getValue()));\n        break;\n      case CodeConstants.opc_astore:\n        data.setVariable(instr.operand(0), stack.pop());\n        break;\n      case CodeConstants.opc_dup:\n      case CodeConstants.opc_dup_x1:\n      case CodeConstants.opc_dup_x2:\n        int depth1 = 88 - instr.opcode;\n        stack.insertByOffset(depth1, stack.getByOffset(-1).copy());\n        break;\n      case CodeConstants.opc_dup2:\n      case CodeConstants.opc_dup2_x1:\n      case CodeConstants.opc_dup2_x2:\n        int depth2 = 90 - instr.opcode;\n        stack.insertByOffset(depth2, stack.getByOffset(-2).copy());\n        stack.insertByOffset(depth2, stack.getByOffset(-1).copy());\n        break;\n      case CodeConstants.opc_swap:\n        var1 = stack.pop();\n        stack.insertByOffset(-1, var1);\n        break;\n      case CodeConstants.opc_getfield:\n        stack.pop();\n      case CodeConstants.opc_getstatic:\n        ck = pool.getLinkConstant(instr.operand(0));\n        var1 = new VarType(ck.descriptor);\n        stack.push(var1);\n        if (var1.getStackSize() == 2) {\n          stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));\n        }\n        break;\n      case CodeConstants.opc_putfield:\n        stack.pop();\n      case CodeConstants.opc_putstatic:\n        ck = pool.getLinkConstant(instr.operand(0));\n        var1 = new VarType(ck.descriptor);\n        stack.pop(var1.getStackSize());\n        break;\n      case CodeConstants.opc_invokevirtual:\n      case CodeConstants.opc_invokespecial:\n      case CodeConstants.opc_invokeinterface:\n        stack.pop();\n      case CodeConstants.opc_invokestatic:\n      case CodeConstants.opc_invokedynamic:\n        if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) {\n          ck = pool.getLinkConstant(instr.operand(0));\n          MethodDescriptor md = MethodDescriptor.parseDescriptor(ck.descriptor);\n          for (int i = 0; i < md.params.length; i++) {\n            stack.pop(md.params[i].getStackSize());\n          }\n          if (md.ret.getType() != CodeConstants.TYPE_VOID) {\n            stack.push(md.ret);\n            if (md.ret.getStackSize() == 2) {\n              stack.push(new VarType(CodeConstants.TYPE_GROUP2EMPTY));\n            }\n          }\n        }\n        break;\n      case CodeConstants.opc_new:\n        cn = pool.getPrimitiveConstant(instr.operand(0));\n        stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()));\n        break;\n      case CodeConstants.opc_newarray:\n        stack.pop();\n        stack.push(new VarType(arr_type[instr.operand(0) - 4], 1).resizeArrayDim(1));\n        break;\n      case CodeConstants.opc_athrow:\n        var1 = stack.pop();\n        stack.clear();\n        stack.push(var1);\n        break;\n      case CodeConstants.opc_checkcast:\n      case CodeConstants.opc_instanceof:\n        stack.pop();\n        cn = pool.getPrimitiveConstant(instr.operand(0));\n        stack.push(new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString()));\n        break;\n      case CodeConstants.opc_anewarray:\n      case CodeConstants.opc_multianewarray:\n        int dimensions = (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.operand(1);\n        stack.pop(dimensions);\n        cn = pool.getPrimitiveConstant(instr.operand(0));\n        if (cn.isArray) {\n          var1 = new VarType(CodeConstants.TYPE_OBJECT, 0, cn.getString());\n          var1 = var1.resizeArrayDim(var1.getArrayDim() + dimensions);\n          stack.push(var1);\n        }\n        else {\n          stack.push(new VarType(CodeConstants.TYPE_OBJECT, dimensions, cn.getString()));\n        }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/AssertProcessor.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.SecondaryFunctionsHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic final class AssertProcessor {\n\n  private static final VarType CLASS_ASSERTION_ERROR = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/AssertionError\");\n\n  public static void buildAssertions(ClassNode node) {\n\n    ClassWrapper wrapper = node.getWrapper();\n\n    StructField field = findAssertionField(node);\n\n    if (field != null) {\n\n      String key = InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor());\n\n      boolean res = false;\n\n      for (MethodWrapper meth : wrapper.getMethods()) {\n        RootStatement root = meth.root;\n        if (root != null) {\n          res |= replaceAssertions(root, wrapper.getClassStruct().qualifiedName, key);\n        }\n      }\n\n      if (res) {\n        // hide the helper field\n        wrapper.getHiddenMembers().add(key);\n      }\n    }\n  }\n\n  private static StructField findAssertionField(ClassNode node) {\n\n    ClassWrapper wrapper = node.getWrapper();\n\n    boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);\n\n    for (StructField fd : wrapper.getClassStruct().getFields()) {\n\n      String keyField = InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor());\n\n      // initializer exists\n      if (wrapper.getStaticFieldInitializers().containsKey(keyField)) {\n\n        // access flags set\n        if (fd.hasModifier(CodeConstants.ACC_STATIC) && fd.hasModifier(CodeConstants.ACC_FINAL) && (noSynthFlag || fd.isSynthetic())) {\n\n          // field type boolean\n          FieldDescriptor fdescr = FieldDescriptor.parseDescriptor(fd.getDescriptor());\n          if (VarType.VARTYPE_BOOLEAN.equals(fdescr.type)) {\n\n            Exprent initializer = wrapper.getStaticFieldInitializers().getWithKey(keyField);\n            if (initializer.type == Exprent.EXPRENT_FUNCTION) {\n              FunctionExprent fexpr = (FunctionExprent)initializer;\n\n              if (fexpr.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT &&\n                  fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_INVOCATION) {\n\n                InvocationExprent invexpr = (InvocationExprent)fexpr.getLstOperands().get(0);\n\n                if (invexpr.getInstance() != null &&\n                    invexpr.getInstance().type == Exprent.EXPRENT_CONST &&\n                    \"desiredAssertionStatus\".equals(invexpr.getName()) &&\n                    \"java/lang/Class\".equals(invexpr.getClassName()) &&\n                    invexpr.getParameters().isEmpty()) {\n\n                  ConstExprent cexpr = (ConstExprent)invexpr.getInstance();\n                  if (VarType.VARTYPE_CLASS.equals(cexpr.getConstType())) {\n\n                    ClassNode nd = node;\n                    while (nd != null) {\n                      if (nd.getWrapper().getClassStruct().qualifiedName.equals(cexpr.getValue())) {\n                        break;\n                      }\n                      nd = nd.parent;\n                    }\n\n                    if (nd != null) { // found enclosing class with the same name\n                      return fd;\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n\n\n    return null;\n  }\n\n\n  private static boolean replaceAssertions(Statement statement, String classname, String key) {\n\n    boolean res = false;\n\n    for (Statement st : statement.getStats()) {\n      res |= replaceAssertions(st, classname, key);\n    }\n\n    boolean replaced = true;\n    while (replaced) {\n      replaced = false;\n\n      for (Statement st : statement.getStats()) {\n        if (st.type == StatementType.IF) {\n          if (replaceAssertion(statement, (IfStatement)st, classname, key)) {\n            replaced = true;\n            break;\n          }\n        }\n      }\n\n      res |= replaced;\n    }\n\n    return res;\n  }\n\n  private static boolean replaceAssertion(Statement parent, IfStatement stat, String classname, String key) {\n\n    boolean throwInIf = true;\n    Statement ifstat = stat.getIfstat();\n    InvocationExprent throwError = isAssertionError(ifstat);\n\n    if (throwError == null) {\n      //check else:\n      Statement elsestat = stat.getElsestat();\n      throwError = isAssertionError(elsestat);\n      if (throwError == null) {\n          return false;\n      }\n      else {\n          throwInIf = false;\n      }\n    }\n\n\n    Object[] exprres = getAssertionExprent(stat.getHeadexprent().getCondition().copy(), classname, key, throwInIf);\n    if (!(Boolean)exprres[1]) {\n      return false;\n    }\n\n    List<Exprent> lstParams = new ArrayList<>();\n\n    Exprent ascond = null, retcond = null;\n    if (throwInIf) {\n      if (exprres[0] != null) {\n        ascond = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, (Exprent)exprres[0], throwError.bytecode);\n        retcond = SecondaryFunctionsHelper.propagateBoolNot(ascond);\n      }\n    }\n    else {\n        ascond =  (Exprent) exprres[0];\n        retcond = ascond;\n    }\n\n\n    lstParams.add(retcond == null ? ascond : retcond);\n    if (!throwError.getParameters().isEmpty()) {\n      lstParams.add(throwError.getParameters().get(0));\n    }\n\n    AssertExprent asexpr = new AssertExprent(lstParams);\n\n    Statement newstat = new BasicBlockStatement(new BasicBlock(\n      DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));\n    newstat.setExprents(Arrays.asList(new Exprent[]{asexpr}));\n\n    Statement first = stat.getFirst();\n\n    if (stat.iftype == IfStatement.IFTYPE_IFELSE || (first.getExprents() != null &&\n                                                     !first.getExprents().isEmpty())) {\n\n      first.removeSuccessor(stat.getIfEdge());\n      first.removeSuccessor(stat.getElseEdge());\n\n      List<Statement> lstStatements = new ArrayList<>();\n      if (first.getExprents() != null && !first.getExprents().isEmpty()) {\n        lstStatements.add(first);\n      }\n      lstStatements.add(newstat);\n      if (stat.iftype == IfStatement.IFTYPE_IFELSE) {\n        if (throwInIf) {\n          lstStatements.add(stat.getElsestat());\n        }\n        else {\n          lstStatements.add(stat.getIfstat());\n        }\n      }\n\n      SequenceStatement sequence = new SequenceStatement(lstStatements);\n      sequence.setAllParent();\n\n      for (int i = 0; i < sequence.getStats().size() - 1; i++) {\n        sequence.getStats().get(i).addSuccessor(new StatEdge(EdgeType.REGULAR,\n                                                             sequence.getStats().get(i), sequence.getStats().get(i + 1)));\n      }\n\n      if (stat.iftype == IfStatement.IFTYPE_IFELSE || !throwInIf) {\n        Statement stmts;\n        if (throwInIf) {\n          stmts = stat.getElsestat();\n        }\n        else {\n          stmts = stat.getIfstat();\n        }\n\n        List<StatEdge> lstSuccs = stmts.getAllSuccessorEdges();\n        if (!lstSuccs.isEmpty()) {\n          StatEdge endedge = lstSuccs.get(0);\n          if (endedge.closure == stat) {\n            sequence.addLabeledEdge(endedge);\n          }\n        }\n      }\n\n      newstat = sequence;\n    }\n\n    newstat.getVarDefinitions().addAll(stat.getVarDefinitions());\n    parent.replaceStatement(stat, newstat);\n\n    return true;\n  }\n\n  private static InvocationExprent isAssertionError(Statement stat) {\n\n    if (stat == null || stat.getExprents() == null || stat.getExprents().size() != 1) {\n      return null;\n    }\n\n    Exprent expr = stat.getExprents().get(0);\n\n    if (expr.type == Exprent.EXPRENT_EXIT) {\n      ExitExprent exexpr = (ExitExprent)expr;\n      if (exexpr.getExitType() == ExitExprent.EXIT_THROW && exexpr.getValue().type == Exprent.EXPRENT_NEW) {\n        NewExprent nexpr = (NewExprent)exexpr.getValue();\n        if (CLASS_ASSERTION_ERROR.equals(nexpr.getNewType()) && nexpr.getConstructor() != null) {\n          return nexpr.getConstructor();\n        }\n      }\n    }\n\n    return null;\n  }\n\n  private static Object[] getAssertionExprent(Exprent exprent, String classname, String key, boolean throwInIf) {\n\n    if (exprent.type == Exprent.EXPRENT_FUNCTION) {\n      int desiredOperation = FunctionExprent.FUNCTION_CADD;\n      if (!throwInIf) {\n        desiredOperation = FunctionExprent.FUNCTION_COR;\n      }\n\n      FunctionExprent fexpr = (FunctionExprent)exprent;\n      if (fexpr.getFuncType() == desiredOperation) {\n\n        for (int i = 0; i < 2; i++) {\n          Exprent param = fexpr.getLstOperands().get(i);\n\n          if (isAssertionField(param, classname, key, throwInIf)) {\n            return new Object[]{fexpr.getLstOperands().get(1 - i), true};\n          }\n        }\n\n        for (int i = 0; i < 2; i++) {\n          Exprent param = fexpr.getLstOperands().get(i);\n\n          Object[] res = getAssertionExprent(param, classname, key, throwInIf);\n          if ((Boolean)res[1]) {\n            if (param != res[0]) {\n              fexpr.getLstOperands().set(i, (Exprent)res[0]);\n            }\n            return new Object[]{fexpr, true};\n          }\n        }\n      }\n      else if (isAssertionField(fexpr, classname, key, throwInIf)) {\n        // assert false;\n        return new Object[]{null, true};\n      }\n    }\n\n    return new Object[]{exprent, false};\n  }\n\n  private static boolean isAssertionField(Exprent exprent, String classname, String key, boolean throwInIf) {\n    if (throwInIf) {\n      if (exprent.type == Exprent.EXPRENT_FUNCTION) {\n        FunctionExprent fparam = (FunctionExprent)exprent;\n        if (fparam.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT &&\n            fparam.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {\n          FieldExprent fdparam = (FieldExprent)fparam.getLstOperands().get(0);\n          return classname.equals(fdparam.getClassname()) &&\n                 key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));\n        }\n      }\n    }\n    else {\n      if (exprent.type == Exprent.EXPRENT_FIELD) {\n        FieldExprent fdparam = (FieldExprent) exprent;\n        return classname.equals(fdparam.getClassname()) &&\n               key.equals(InterpreterUtil.makeUniqueKey(fdparam.getName(), fdparam.getDescriptor().descriptorString));\n      }\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/CancellationManager.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.annotations.ApiStatus;\nimport org.jetbrains.annotations.NotNull;\n\n@ApiStatus.Experimental\npublic interface CancellationManager {\n  /**\n   * @throws CanceledException if the process has been canceled.\n   */\n  void checkCanceled() throws CanceledException;\n\n  /**\n   * Called every time the body of a new method is started to be decompiled\n   */\n  void startMethod(String className, String methodName);\n\n  /**\n   * Called every time the method decompilation is finished\n   */\n  void finishMethod(String className, String methodName);\n\n  @ApiStatus.Experimental\n  class CanceledException extends RuntimeException {\n\n    public CanceledException(@NotNull Throwable cause) {\n      super(cause);\n    }\n\n    public CanceledException() {\n      super();\n    }\n  }\n\n  @ApiStatus.Experimental\n  class TimeExceedException extends CanceledException {\n  }\n\n  static CancellationManager getSimpleWithTimeout(int maxMethodTimeoutSec) {\n    return new TimeoutCancellationManager(maxMethodTimeoutSec);\n  }\n\n  class TimeoutCancellationManager implements CancellationManager {\n    private final long maxMilis;\n    private long startMilis = 0;\n\n    protected TimeoutCancellationManager(int maxMethodTimeoutSec) {\n      this.maxMilis = maxMethodTimeoutSec * 1000L;\n    }\n\n    @Override\n    public void checkCanceled() throws CanceledException {\n      if (maxMilis <= 0 || startMilis <= 0) {\n        return;\n      }\n      long timeMillis = System.currentTimeMillis();\n      if (timeMillis - startMilis > maxMilis) {\n        throw new TimeExceedException();\n      }\n    }\n\n    @Override\n    public void startMethod(String className, String methodName) {\n      startMilis = System.currentTimeMillis();\n    }\n\n    @Override\n    public void finishMethod(String className, String methodName) {\n      startMilis = 0;\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic final class ClassReference14Processor {\n  private static final ExitExprent BODY_EXPR;\n  private static final ExitExprent HANDLER_EXPR;\n\n  static {\n    InvocationExprent invFor = new InvocationExprent();\n    invFor.setName(\"forName\");\n    invFor.setClassName(\"java/lang/Class\");\n    invFor.setStringDescriptor(\"(Ljava/lang/String;)Ljava/lang/Class;\");\n    invFor.setDescriptor(MethodDescriptor.parseDescriptor(\"(Ljava/lang/String;)Ljava/lang/Class;\"));\n    invFor.setStatic(true);\n    invFor.setParameters(Collections.singletonList(new VarExprent(0, VarType.VARTYPE_STRING, null)));\n    BODY_EXPR = new ExitExprent(ExitExprent.EXIT_RETURN, invFor, VarType.VARTYPE_CLASS, null);\n\n    InvocationExprent ctor = new InvocationExprent();\n    ctor.setName(CodeConstants.INIT_NAME);\n    ctor.setClassName(\"java/lang/NoClassDefFoundError\");\n    ctor.setStringDescriptor(\"()V\");\n    ctor.setFuncType(InvocationExprent.TYPE_INIT);\n    ctor.setDescriptor(MethodDescriptor.parseDescriptor(\"()V\"));\n    NewExprent newExpr = new NewExprent(new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/NoClassDefFoundError\"), new ArrayList<>(), null);\n    newExpr.setConstructor(ctor);\n    InvocationExprent invCause = new InvocationExprent();\n    invCause.setName(\"initCause\");\n    invCause.setClassName(\"java/lang/NoClassDefFoundError\");\n    invCause.setStringDescriptor(\"(Ljava/lang/Throwable;)Ljava/lang/Throwable;\");\n    invCause.setDescriptor(MethodDescriptor.parseDescriptor(\"(Ljava/lang/Throwable;)Ljava/lang/Throwable;\"));\n    invCause.setInstance(newExpr);\n    invCause.setParameters(\n      Collections.singletonList(new VarExprent(2, new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/ClassNotFoundException\"), null)));\n    HANDLER_EXPR = new ExitExprent(ExitExprent.EXIT_THROW, invCause, null, null);\n  }\n\n  public static void processClassReferences(ClassNode node) {\n    // find the synthetic method Class class$(String) if present\n    Map<ClassWrapper, MethodWrapper> mapClassMeths = new HashMap<>();\n    mapClassMethods(node, mapClassMeths);\n    if (mapClassMeths.isEmpty()) {\n      return;\n    }\n\n    Set<ClassWrapper> setFound = new HashSet<>();\n    processClassRec(node, mapClassMeths, setFound);\n\n    if (!setFound.isEmpty()) {\n      for (ClassWrapper wrp : setFound) {\n        StructMethod mt = mapClassMeths.get(wrp).methodStruct;\n        wrp.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));\n      }\n    }\n  }\n\n  private static void processClassRec(ClassNode node, Map<ClassWrapper, MethodWrapper> mapClassMeths, Set<? super ClassWrapper> setFound) {\n    ClassWrapper wrapper = node.getWrapper();\n\n    // search code\n    for (MethodWrapper meth : wrapper.getMethods()) {\n      RootStatement root = meth.root;\n      if (root != null) {\n        DirectGraph graph = meth.getOrBuildGraph();\n        graph.iterateExprents(exprent -> {\n          for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {\n            if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {\n              setFound.add(ent.getKey());\n            }\n          }\n          return 0;\n        });\n      }\n    }\n\n    // search initializers\n    for (int j = 0; j < 2; j++) {\n      VBStyleCollection<Exprent, String> initializers =\n        j == 0 ? wrapper.getStaticFieldInitializers() : wrapper.getDynamicFieldInitializers();\n\n      for (int i = 0; i < initializers.size(); i++) {\n        for (Entry<ClassWrapper, MethodWrapper> ent : mapClassMeths.entrySet()) {\n          Exprent exprent = initializers.get(i);\n          if (replaceInvocations(exprent, ent.getKey(), ent.getValue())) {\n            setFound.add(ent.getKey());\n          }\n\n          String cl = isClass14Invocation(exprent, ent.getKey(), ent.getValue());\n          if (cl != null) {\n            initializers.set(i, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), exprent.bytecode));\n            setFound.add(ent.getKey());\n          }\n        }\n      }\n    }\n\n    // iterate nested classes\n    for (ClassNode nd : node.nested) {\n      processClassRec(nd, mapClassMeths, setFound);\n    }\n  }\n\n  private static void mapClassMethods(ClassNode node, Map<ClassWrapper, MethodWrapper> map) {\n    boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);\n\n    ClassWrapper wrapper = node.getWrapper();\n\n    for (MethodWrapper method : wrapper.getMethods()) {\n      StructMethod mt = method.methodStruct;\n\n      if ((noSynthFlag || mt.isSynthetic()) &&\n          mt.getDescriptor().equals(\"(Ljava/lang/String;)Ljava/lang/Class;\") &&\n          mt.hasModifier(CodeConstants.ACC_STATIC)) {\n\n        RootStatement root = method.root;\n        if (root != null && root.getFirst().type == StatementType.TRY_CATCH) {\n          CatchStatement cst = (CatchStatement)root.getFirst();\n          if (cst.getStats().size() == 2 && cst.getFirst().type == StatementType.BASIC_BLOCK &&\n              cst.getStats().get(1).type == StatementType.BASIC_BLOCK &&\n              cst.getVars().get(0).getVarType().equals(new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/ClassNotFoundException\"))) {\n\n            BasicBlockStatement body = (BasicBlockStatement)cst.getFirst();\n            BasicBlockStatement handler = (BasicBlockStatement)cst.getStats().get(1);\n\n            if (body.getExprents().size() == 1 && handler.getExprents().size() == 1) {\n              if (BODY_EXPR.equals(body.getExprents().get(0)) &&\n                  HANDLER_EXPR.equals(handler.getExprents().get(0))) {\n                map.put(wrapper, method);\n                break;\n              }\n            }\n          }\n        }\n      }\n    }\n\n    // iterate nested classes\n    for (ClassNode nd : node.nested) {\n      mapClassMethods(nd, map);\n    }\n  }\n\n  private static boolean replaceInvocations(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {\n    boolean res = false;\n\n    while (true) {\n      boolean found = false;\n\n      for (Exprent expr : exprent.getAllExprents()) {\n        String cl = isClass14Invocation(expr, wrapper, meth);\n        if (cl != null) {\n          exprent.replaceExprent(expr, new ConstExprent(VarType.VARTYPE_CLASS, cl.replace('.', '/'), expr.bytecode));\n          found = true;\n          res = true;\n          break;\n        }\n\n        res |= replaceInvocations(expr, wrapper, meth);\n      }\n\n      if (!found) {\n        break;\n      }\n    }\n\n    return res;\n  }\n\n  private static String isClass14Invocation(Exprent exprent, ClassWrapper wrapper, MethodWrapper meth) {\n    if (exprent.type == Exprent.EXPRENT_FUNCTION) {\n      FunctionExprent fexpr = (FunctionExprent)exprent;\n      if (fexpr.getFuncType() == FunctionExprent.FUNCTION_IIF) {\n        if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) {\n          FunctionExprent headexpr = (FunctionExprent)fexpr.getLstOperands().get(0);\n          if (headexpr.getFuncType() == FunctionExprent.FUNCTION_EQ) {\n            if (headexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD &&\n                headexpr.getLstOperands().get(1).type == Exprent.EXPRENT_CONST &&\n                ((ConstExprent)headexpr.getLstOperands().get(1)).getConstType().equals(VarType.VARTYPE_NULL)) {\n\n              FieldExprent field = (FieldExprent)headexpr.getLstOperands().get(0);\n              ClassNode fieldnode = DecompilerContext.getClassProcessor().getMapRootClasses().get(field.getClassname());\n\n              if (fieldnode != null && fieldnode.classStruct.qualifiedName.equals(wrapper.getClassStruct().qualifiedName)) { // source class\n                StructField fd =\n                  wrapper.getClassStruct().getField(field.getName(), field.getDescriptor().descriptorString);  // FIXME: can be null! why??\n\n                if (fd != null && fd.hasModifier(CodeConstants.ACC_STATIC) &&\n                    (fd.isSynthetic() || DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET))) {\n\n                  if (fexpr.getLstOperands().get(1).type == Exprent.EXPRENT_ASSIGNMENT && fexpr.getLstOperands().get(2).equals(field)) {\n                    AssignmentExprent asexpr = (AssignmentExprent)fexpr.getLstOperands().get(1);\n\n                    if (asexpr.getLeft().equals(field) && asexpr.getRight().type == Exprent.EXPRENT_INVOCATION) {\n                      InvocationExprent invexpr = (InvocationExprent)asexpr.getRight();\n\n                      if (invexpr.getClassName().equals(wrapper.getClassStruct().qualifiedName) &&\n                          invexpr.getName().equals(meth.methodStruct.getName()) &&\n                          invexpr.getStringDescriptor().equals(meth.methodStruct.getDescriptor())) {\n\n                        if (invexpr.getParameters().get(0).type == Exprent.EXPRENT_CONST) {\n                          wrapper.getHiddenMembers()\n                            .add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));  // hide synthetic field\n                          return ((ConstExprent)invexpr.getParameters().get(0)).getValue().toString();\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/ClassWriter.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TargetInfo;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TypeAnnotation;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TypeAnnotationWriteHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;\nimport org.jetbrains.java.decompiler.struct.*;\nimport org.jetbrains.java.decompiler.struct.attr.*;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.Type;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.gen.generics.*;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class ClassWriter {\n  private final PoolInterceptor interceptor;\n\n  public ClassWriter() {\n    interceptor = DecompilerContext.getPoolInterceptor();\n  }\n\n  private static void invokeProcessors(ClassNode node) {\n    ClassWrapper wrapper = node.getWrapper();\n    StructClass cl = wrapper.getClassStruct();\n\n    InitializerProcessor.extractInitializers(wrapper);\n\n    if (node.type == ClassNode.CLASS_ROOT &&\n        !cl.isVersion5() &&\n        DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4)) {\n      ClassReference14Processor.processClassReferences(node);\n    }\n\n    if (cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) {\n      EnumProcessor.clearEnum(wrapper);\n    }\n\n    if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ASSERTIONS)) {\n      AssertProcessor.buildAssertions(node);\n    }\n  }\n\n  public void classLambdaToJava(ClassNode node, TextBuffer buffer, Exprent method_object, int indent, BytecodeMappingTracer origTracer) {\n    ClassWrapper wrapper = node.getWrapper();\n    if (wrapper == null) {\n      return;\n    }\n\n    boolean lambdaToAnonymous = DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS);\n\n    ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);\n    DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node);\n\n    BytecodeMappingTracer tracer = new BytecodeMappingTracer(origTracer.getCurrentSourceLine());\n\n    try {\n      StructClass cl = wrapper.getClassStruct();\n\n      DecompilerContext.getLogger().startWriteClass(node.simpleName);\n\n      if (node.lambdaInformation.is_method_reference) {\n        if (!node.lambdaInformation.is_content_method_static && method_object != null) {\n          // reference to a virtual method\n          buffer.append(method_object.toJava(indent, tracer));\n        }\n        else {\n          // reference to a static method\n          buffer.append(ExprProcessor.getCastTypeName(new VarType(node.lambdaInformation.content_class_name, true), Collections.emptyList()));\n        }\n\n        buffer.append(\"::\")\n          .append(CodeConstants.INIT_NAME.equals(node.lambdaInformation.content_method_name) ? \"new\" : node.lambdaInformation.content_method_name);\n      }\n      else {\n        // lambda method\n        StructMethod mt = cl.getMethod(node.lambdaInformation.content_method_key);\n        MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());\n        MethodDescriptor md_content = MethodDescriptor.parseDescriptor(node.lambdaInformation.content_method_descriptor);\n        MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(node.lambdaInformation.method_descriptor);\n\n        List<TypeAnnotation> parameterTypeAnnotations = TargetInfo.FormalParameterTarget.extract(TypeAnnotation.listFrom(mt));\n        boolean explicitlyTyped = !parameterTypeAnnotations.isEmpty();\n\n        if (!lambdaToAnonymous) {\n          buffer.append('(');\n\n          boolean firstParameter = true;\n          int index = node.lambdaInformation.is_content_method_static ? 0 : 1;\n          int start_index = md_content.params.length - md_lambda.params.length;\n\n          for (int i = 0; i < md_content.params.length; i++) {\n            if (i >= start_index) {\n              if (!firstParameter) {\n                buffer.append(\", \");\n              }\n\n              if (explicitlyTyped) {\n                List<TypeAnnotation> iParameterTypeAnnotations = TargetInfo.FormalParameterTarget.extract(parameterTypeAnnotations, i);\n                VarType type = md_content.params[i];\n                buffer.append(ExprProcessor.getCastTypeName(type, TypeAnnotationWriteHelper.create(iParameterTypeAnnotations)));\n                buffer.append(' ');\n              }\n\n              String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));\n              buffer.append(parameterName == null ? \"param\" + index : parameterName); // null iff decompiled with errors\n\n              firstParameter = false;\n            }\n\n            index += md_content.params[i].getStackSize();\n          }\n\n          buffer.append(\") ->\");\n        }\n\n        buffer.append(\" {\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n\n        methodLambdaToJava(node, wrapper, mt, buffer, indent + 1, !lambdaToAnonymous, tracer);\n\n        buffer.appendIndent(indent).append(\"}\");\n\n        addTracer(cl, mt, tracer);\n      }\n    }\n    finally {\n      DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode);\n    }\n\n    DecompilerContext.getLogger().endWriteClass();\n  }\n\n  public void classToJava(ClassNode node, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {\n    ClassNode outerNode = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);\n    DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, node);\n\n    int startLine = tracer != null ? tracer.getCurrentSourceLine() : 0;\n    BytecodeMappingTracer dummy_tracer = new BytecodeMappingTracer(startLine);\n\n    try {\n      // last minute processing\n      invokeProcessors(node);\n\n      ClassWrapper wrapper = node.getWrapper();\n      StructClass cl = wrapper.getClassStruct();\n\n      DecompilerContext.getLogger().startWriteClass(cl.qualifiedName);\n\n      // write class definition\n      int start_class_def = buffer.length();\n      writeClassDefinition(node, buffer, indent);\n\n      boolean hasContent = false;\n      boolean enumFields = false;\n\n      dummy_tracer.incrementCurrentSourceLine(buffer.countLines(start_class_def));\n\n      List<StructRecordComponent> components = cl.getRecordComponents();\n\n      for (StructField fd : cl.getFields()) {\n        boolean hide = fd.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||\n                       wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));\n        if (hide) continue;\n\n        if (components != null && fd.getAccessFlags() == (CodeConstants.ACC_FINAL | CodeConstants.ACC_PRIVATE) &&\n            components.stream().anyMatch(c -> c.getName().equals(fd.getName()) && c.getDescriptor().equals(fd.getDescriptor()))) {\n          // Record component field: skip it\n          continue;\n        }\n\n        boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);\n        if (isEnum) {\n          if (enumFields) {\n            buffer.append(',').appendLineSeparator();\n            dummy_tracer.incrementCurrentSourceLine();\n          }\n          enumFields = true;\n        }\n        else if (enumFields) {\n          buffer.append(';');\n          buffer.appendLineSeparator();\n          buffer.appendLineSeparator();\n          dummy_tracer.incrementCurrentSourceLine(2);\n          enumFields = false;\n        }\n\n        fieldToJava(wrapper, cl, fd, buffer, indent + 1, dummy_tracer); // FIXME: insert real tracer\n\n        hasContent = true;\n      }\n\n      if (enumFields) {\n        buffer.append(';').appendLineSeparator();\n        dummy_tracer.incrementCurrentSourceLine();\n      }\n\n      // FIXME: fields don't matter at the moment\n      startLine += buffer.countLines(start_class_def);\n\n      // methods\n      for (StructMethod mt : cl.getMethods()) {\n        boolean hide = mt.isSynthetic() && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||\n                       mt.hasModifier(CodeConstants.ACC_BRIDGE) && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_BRIDGE) ||\n                       wrapper.getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));\n        if (hide) continue;\n\n        int position = buffer.length();\n        int storedLine = startLine;\n        if (hasContent) {\n          buffer.appendLineSeparator();\n          startLine++;\n        }\n        BytecodeMappingTracer method_tracer = new BytecodeMappingTracer(startLine);\n        boolean methodSkipped = !methodToJava(node, mt, buffer, indent + 1, method_tracer);\n        if (!methodSkipped) {\n          hasContent = true;\n          addTracer(cl, mt, method_tracer);\n          startLine = method_tracer.getCurrentSourceLine();\n        }\n        else {\n          buffer.setLength(position);\n          startLine = storedLine;\n        }\n      }\n\n      // member classes\n      for (ClassNode inner : node.nested) {\n        if (inner.type == ClassNode.CLASS_MEMBER) {\n          StructClass innerCl = inner.classStruct;\n          boolean isSynthetic = (inner.access & CodeConstants.ACC_SYNTHETIC) != 0 || innerCl.isSynthetic();\n          boolean hide = isSynthetic && DecompilerContext.getOption(IFernflowerPreferences.REMOVE_SYNTHETIC) ||\n                         wrapper.getHiddenMembers().contains(innerCl.qualifiedName);\n          if (hide) continue;\n\n          if (hasContent) {\n            buffer.appendLineSeparator();\n            startLine++;\n          }\n          BytecodeMappingTracer class_tracer = new BytecodeMappingTracer(startLine);\n          classToJava(inner, buffer, indent + 1, class_tracer);\n          startLine = buffer.countLines();\n\n          hasContent = true;\n        }\n      }\n\n      buffer.appendIndent(indent).append('}');\n\n      if (node.type != ClassNode.CLASS_ANONYMOUS) {\n        buffer.appendLineSeparator();\n      }\n    }\n    finally {\n      DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_NODE, outerNode);\n    }\n\n    DecompilerContext.getLogger().endWriteClass();\n  }\n\n  @SuppressWarnings(\"SpellCheckingInspection\")\n  private static boolean isSyntheticRecordMethod(StructClass cl, StructMethod mt, TextBuffer code) {\n    if (cl.getRecordComponents() != null) {\n      String name = mt.getName(), descriptor = mt.getDescriptor();\n      if (name.equals(\"equals\") && descriptor.equals(\"(Ljava/lang/Object;)Z\") ||\n          name.equals(\"hashCode\") && descriptor.equals(\"()I\") ||\n          name.equals(\"toString\") && descriptor.equals(\"()Ljava/lang/String;\")) {\n        if (code.countLines() == 1) {\n          String str = code.toString().trim();\n          return str.startsWith(\"return this.\" + name + \"<invokedynamic>(this\");\n        }\n      }\n    }\n    return false;\n  }\n\n  private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent) {\n    if (node.type == ClassNode.CLASS_ANONYMOUS) {\n      buffer.append(\" {\").appendLineSeparator();\n      return;\n    }\n\n    ClassWrapper wrapper = node.getWrapper();\n    StructClass cl = wrapper.getClassStruct();\n\n    int flags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access;\n    boolean isDeprecated = cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);\n    boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC);\n    boolean isEnum = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM) && (flags & CodeConstants.ACC_ENUM) != 0;\n    boolean isInterface = (flags & CodeConstants.ACC_INTERFACE) != 0;\n    boolean isAnnotation = (flags & CodeConstants.ACC_ANNOTATION) != 0;\n\n    if (isDeprecated) {\n      appendDeprecation(buffer, indent);\n    }\n\n    if (interceptor != null) {\n      String oldName = interceptor.getOldName(cl.qualifiedName);\n      appendRenameComment(buffer, oldName, MType.CLASS, indent);\n    }\n\n    if (isSynthetic) {\n      appendComment(buffer, \"synthetic class\", indent);\n    }\n\n    appendAnnotations(buffer, indent, cl);\n\n    buffer.appendIndent(indent);\n\n    if (isEnum) {\n      // remove abstract and final flags (JLS 8.9 Enums)\n      flags &= ~CodeConstants.ACC_ABSTRACT;\n      flags &= ~CodeConstants.ACC_FINAL;\n    }\n\n    List<StructRecordComponent> components = cl.getRecordComponents();\n    List<String> permittedSubclassQualifiedNames = cl.getPermittedSubclasses();\n\n    if (components != null) {\n      // records are implicitly final\n      flags &= ~CodeConstants.ACC_FINAL;\n    }\n\n    appendModifiers(buffer, flags, CLASS_ALLOWED, isInterface, CLASS_EXCLUDED);\n\n    if (permittedSubclassQualifiedNames != null && !isEnum) {\n      buffer.append(\"sealed \");\n    }\n    else if (node.isNonSealed()) {\n      buffer.append(\"non-sealed \");\n    }\n\n    if (isEnum) {\n      buffer.append(\"enum \");\n    }\n    else if (isInterface) {\n      if (isAnnotation) {\n        buffer.append('@');\n      }\n      buffer.append(\"interface \");\n    }\n    else if (components != null) {\n      buffer.append(\"record \");\n    }\n    else {\n      buffer.append(\"class \");\n    }\n    buffer.append(node.simpleName);\n\n    List<TypeAnnotation> typeAnnotations = TypeAnnotation.listFrom(cl);\n\n    GenericClassDescriptor descriptor = getGenericClassDescriptor(cl);\n    if (descriptor != null && !descriptor.fparameters.isEmpty()) {\n      appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds, typeAnnotations);\n    }\n\n    if (components != null) {\n      buffer.append('(');\n      for (int i = 0; i < components.size(); i++) {\n        StructRecordComponent cd = components.get(i);\n        if (i > 0) {\n          buffer.append(\", \");\n        }\n        boolean varArgComponent = i == components.size() - 1 && isVarArgRecord(cl);\n        recordComponentToJava(cd, buffer, varArgComponent);\n      }\n      buffer.append(')');\n    }\n\n    buffer.append(' ');\n\n    if (!isEnum && !isInterface && components == null && cl.superClass != null) {\n      VarType supertype = new VarType(cl.superClass.getString(), true);\n      List<TypeAnnotation> extendsTypeAnnotations = TargetInfo.SupertypeTarget.extractExtends(typeAnnotations);\n      if (!VarType.VARTYPE_OBJECT.equals(supertype)) {\n        buffer.append(\"extends \");\n        if (descriptor != null) {\n          buffer.append(GenericMain.getGenericCastTypeName(\n            descriptor.superclass,\n            TypeAnnotationWriteHelper.create(extendsTypeAnnotations))\n          );\n        }\n        else {\n          buffer.append(ExprProcessor.getCastTypeName(supertype, TypeAnnotationWriteHelper.create(extendsTypeAnnotations)));\n        }\n        buffer.append(' ');\n      }\n    }\n\n    if (!isAnnotation) {\n      int[] interfaces = cl.getInterfaces();\n      if (interfaces.length > 0) {\n        buffer.append(isInterface ? \"extends \" : \"implements \");\n        for (int i = 0; i < interfaces.length; i++) {\n          if (i > 0) {\n            buffer.append(\", \");\n          }\n          List<TypeAnnotation> superTypeAnnotations = TargetInfo.SupertypeTarget.extract(typeAnnotations, i);\n          if (descriptor != null) {\n            buffer.append(GenericMain.getGenericCastTypeName(\n              descriptor.superinterfaces.get(i),\n              TypeAnnotationWriteHelper.create(superTypeAnnotations))\n            );\n          }\n          else {\n            buffer.append(ExprProcessor.getCastTypeName(\n              new VarType(cl.getInterface(i), true),\n              TypeAnnotationWriteHelper.create(superTypeAnnotations))\n            );\n          }\n        }\n        buffer.append(' ');\n      }\n    }\n\n    if (permittedSubclassQualifiedNames != null && !permittedSubclassQualifiedNames.isEmpty()) {\n      Set<String> qualifiedNested = node.nested.stream()\n        .map(nestedNode -> nestedNode.classStruct.qualifiedName)\n        .collect(Collectors.toSet());\n      boolean allSubClassesAreNested = qualifiedNested.containsAll(permittedSubclassQualifiedNames);\n      if (!allSubClassesAreNested) { // only generate permits lists for non-nested classes\n        buffer.append(\"permits \");\n        for (int i = 0; i < permittedSubclassQualifiedNames.size(); i++) {\n          String qualifiedName = permittedSubclassQualifiedNames.get(i);\n          if (i > 0) {\n            buffer.append(\", \");\n          }\n          String nestedName = DecompilerContext.getImportCollector().getNestedName(qualifiedName);\n          buffer.append(nestedName);\n        }\n        buffer.append(' ');\n      }\n    }\n    buffer.append('{').appendLineSeparator();\n  }\n\n  private void fieldToJava(ClassWrapper wrapper, StructClass cl, StructField fd, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {\n    int start = buffer.length();\n    boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE);\n    boolean isDeprecated = fd.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);\n    boolean isEnum = fd.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);\n\n    if (isDeprecated) {\n      appendDeprecation(buffer, indent);\n    }\n\n    if (interceptor != null) {\n      String oldName = interceptor.getOldName(cl.qualifiedName + \" \" + fd.getName() + \" \" + fd.getDescriptor());\n      appendRenameComment(buffer, oldName, MType.FIELD, indent);\n    }\n\n    if (fd.isSynthetic()) {\n      appendComment(buffer, \"synthetic field\", indent);\n    }\n\n    Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);\n    VarType fieldType = fieldTypeData.getKey();\n\n    appendAnnotations(buffer, indent, fd);\n\n    buffer.appendIndent(indent);\n\n    if (!isEnum) {\n      appendModifiers(buffer, fd.getAccessFlags(), FIELD_ALLOWED, isInterface, FIELD_EXCLUDED);\n    }\n\n    GenericFieldDescriptor descriptor = fieldTypeData.getValue();\n\n    final List<TypeAnnotation> typeAnnotations = TypeAnnotation.listFrom(fd);\n\n    if (!isEnum) {\n      if (descriptor != null) {\n        buffer.append(GenericMain.getGenericCastTypeName(descriptor.type, TypeAnnotationWriteHelper.create(typeAnnotations)));\n      }\n      else {\n        buffer.append(ExprProcessor.getCastTypeName(fieldType, TypeAnnotationWriteHelper.create(typeAnnotations)));\n      }\n      buffer.append(' ');\n    }\n\n    buffer.append(fd.getName());\n\n    tracer.incrementCurrentSourceLine(buffer.countLines(start));\n\n    Exprent initializer;\n    if (fd.hasModifier(CodeConstants.ACC_STATIC)) {\n      initializer = wrapper.getStaticFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));\n    }\n    else {\n      initializer = wrapper.getDynamicFieldInitializers().getWithKey(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));\n    }\n    if (initializer != null) {\n      if (isEnum && initializer.type == Exprent.EXPRENT_NEW) {\n        NewExprent expr = (NewExprent)initializer;\n        expr.setEnumConst(true);\n        buffer.append(expr.toJava(indent, tracer));\n      }\n      else {\n        buffer.append(\" = \");\n\n        if (initializer.type == Exprent.EXPRENT_CONST) {\n          ((ConstExprent) initializer).adjustConstType(fieldType);\n        }\n\n        // FIXME: special case field initializer. Can map to more than one method (constructor) and bytecode instruction\n        buffer.append(initializer.toJava(indent, tracer));\n      }\n    }\n    else if (fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_STATIC)) {\n      StructConstantValueAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_CONSTANT_VALUE);\n      if (attr != null) {\n        PrimitiveConstant constant = cl.getPool().getPrimitiveConstant(attr.getIndex());\n        buffer.append(\" = \");\n        buffer.append(new ConstExprent(fieldType, constant.value, null, fd).toJava(indent, tracer));\n      }\n    }\n\n    if (!isEnum) {\n      buffer.append(';').appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n  }\n\n  private static void writeModuleInfoBody(TextBuffer buffer, StructModuleAttribute moduleAttribute) {\n    boolean newLineNeeded = false;\n\n    List<StructModuleAttribute.RequiresEntry> requiresEntries = moduleAttribute.requires;\n    if (!requiresEntries.isEmpty()) {\n      for (StructModuleAttribute.RequiresEntry requires : requiresEntries) {\n        if (!isGenerated(requires.flags)) {\n          buffer.appendIndent(1).append(\"requires \");\n          if ((requires.flags & CodeConstants.ACC_STATIC_PHASE) != 0) buffer.append(\"static \");\n          if ((requires.flags & CodeConstants.ACC_TRANSITIVE) != 0) buffer.append(\"transitive \");\n          buffer.append(requires.moduleName.replace('/', '.')).append(';').appendLineSeparator();\n          newLineNeeded = true;\n        }\n      }\n    }\n\n    List<StructModuleAttribute.ExportsEntry> exportsEntries = moduleAttribute.exports;\n    if (!exportsEntries.isEmpty()) {\n      if (newLineNeeded) buffer.appendLineSeparator();\n      for (StructModuleAttribute.ExportsEntry exports : exportsEntries) {\n        if (!isGenerated(exports.flags)) {\n          buffer.appendIndent(1).append(\"exports \").append(exports.packageName.replace('/', '.'));\n          List<String> exportToModules = exports.exportToModules;\n          if (!exportToModules.isEmpty()) {\n            buffer.append(\" to\").appendLineSeparator();\n            appendFQClassNames(buffer, exportToModules);\n          }\n          buffer.append(';').appendLineSeparator();\n          newLineNeeded = true;\n        }\n      }\n    }\n\n    List<StructModuleAttribute.OpensEntry> opensEntries = moduleAttribute.opens;\n    if (!opensEntries.isEmpty()) {\n      if (newLineNeeded) buffer.appendLineSeparator();\n      for (StructModuleAttribute.OpensEntry opens : opensEntries) {\n        if (!isGenerated(opens.flags)) {\n          buffer.appendIndent(1).append(\"opens \").append(opens.packageName.replace('/', '.'));\n          List<String> opensToModules = opens.opensToModules;\n          if (!opensToModules.isEmpty()) {\n            buffer.append(\" to\").appendLineSeparator();\n            appendFQClassNames(buffer, opensToModules);\n          }\n          buffer.append(';').appendLineSeparator();\n          newLineNeeded = true;\n        }\n      }\n    }\n\n    List<String> usesEntries = moduleAttribute.uses;\n    if (!usesEntries.isEmpty()) {\n      if (newLineNeeded) buffer.appendLineSeparator();\n      for (String uses : usesEntries) {\n        buffer.appendIndent(1).append(\"uses \").append(ExprProcessor.buildJavaClassName(uses)).append(';').appendLineSeparator();\n      }\n      newLineNeeded = true;\n    }\n\n    List<StructModuleAttribute.ProvidesEntry> providesEntries = moduleAttribute.provides;\n    if (!providesEntries.isEmpty()) {\n      if (newLineNeeded) buffer.appendLineSeparator();\n      for (StructModuleAttribute.ProvidesEntry provides : providesEntries) {\n        buffer.appendIndent(1).append(\"provides \").append(ExprProcessor.buildJavaClassName(provides.interfaceName)).append(\" with\").appendLineSeparator();\n        appendFQClassNames(buffer, provides.implementationNames.stream().map(ExprProcessor::buildJavaClassName).collect(Collectors.toList()));\n        buffer.append(';').appendLineSeparator();\n      }\n    }\n  }\n\n  private static boolean isGenerated(int flags) {\n    return (flags & (CodeConstants.ACC_SYNTHETIC | CodeConstants.ACC_MANDATED)) != 0;\n  }\n\n  private static void addTracer(StructClass cls, StructMethod method, BytecodeMappingTracer tracer) {\n    StructLineNumberTableAttribute table = method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LINE_NUMBER_TABLE);\n    tracer.setLineNumberTable(table);\n    String key = InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor());\n    DecompilerContext.getBytecodeSourceMapper().addTracer(cls.qualifiedName, key, tracer);\n  }\n\n  private boolean methodToJava(ClassNode node, StructMethod mt, TextBuffer buffer, int indent, BytecodeMappingTracer tracer) {\n    ClassWrapper wrapper = node.getWrapper();\n    StructClass cl = wrapper.getClassStruct();\n    MethodWrapper methodWrapper = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());\n\n    boolean hideMethod = false;\n    int start_index_method = buffer.length();\n\n    MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);\n    DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper);\n\n    try {\n      boolean isInterface = cl.hasModifier(CodeConstants.ACC_INTERFACE);\n      boolean isAnnotation = cl.hasModifier(CodeConstants.ACC_ANNOTATION);\n      boolean isDeprecated = mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_DEPRECATED);\n      boolean clInit = false, dInit = false;\n\n      MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n\n      int flags = mt.getAccessFlags();\n      if ((flags & CodeConstants.ACC_NATIVE) != 0) {\n        flags &= ~CodeConstants.ACC_STRICT; // compiler bug: a strictfp class sets all methods to strictfp\n      }\n      if (CodeConstants.CLINIT_NAME.equals(mt.getName())) {\n        flags &= CodeConstants.ACC_STATIC; // ignore all modifiers except 'static' in a static initializer\n      }\n\n      if (isDeprecated) {\n        appendDeprecation(buffer, indent);\n      }\n\n      if (interceptor != null) {\n        String oldName = interceptor.getOldName(cl.qualifiedName + \" \" + mt.getName() + \" \" + mt.getDescriptor());\n        appendRenameComment(buffer, oldName, MType.METHOD, indent);\n      }\n\n      boolean isSynthetic = (flags & CodeConstants.ACC_SYNTHETIC) != 0 || mt.hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC);\n      boolean isBridge = (flags & CodeConstants.ACC_BRIDGE) != 0;\n      if (isSynthetic) {\n        appendComment(buffer, \"synthetic method\", indent);\n      }\n      if (isBridge) {\n        appendComment(buffer, \"bridge method\", indent);\n      }\n\n      GenericMethodDescriptor descriptor = null;\n      if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {\n        StructGenericSignatureAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);\n        if (attr != null) {\n          descriptor = GenericMain.parseMethodSignature(attr.getSignature());\n          if (descriptor != null) {\n            long actualParams = md.params.length;\n            List<VarVersionPair> mask = methodWrapper.synthParameters;\n            if (mask != null) {\n              actualParams = mask.stream().filter(Objects::isNull).count();\n            }\n            if (actualParams != descriptor.parameterTypes.size()) {\n              String message = \"Inconsistent generic signature in method \" + mt.getName() + \" \" + mt.getDescriptor() + \" in \" + cl.qualifiedName;\n              DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n              descriptor = null;\n            }\n          }\n        }\n      }\n      appendAnnotations(buffer, indent, mt);\n\n      buffer.appendIndent(indent);\n\n      appendModifiers(buffer, flags, METHOD_ALLOWED, isInterface, METHOD_EXCLUDED);\n\n      if (isInterface && !mt.hasModifier(CodeConstants.ACC_STATIC) && !mt.hasModifier(CodeConstants.ACC_PRIVATE) && mt.containsCode()) {\n        // 'default' modifier (Java 8)\n        buffer.append(\"default \");\n      }\n\n      String name = mt.getName();\n      boolean init = false;\n      if (CodeConstants.INIT_NAME.equals(name)) {\n        if (node.type == ClassNode.CLASS_ANONYMOUS) {\n          name = \"\";\n          dInit = true;\n        }\n        else {\n          name = node.simpleName;\n          init = true;\n        }\n      }\n      else if (CodeConstants.CLINIT_NAME.equals(name)) {\n        name = \"\";\n        clInit = true;\n      }\n\n      boolean throwsExceptions = false;\n      int paramCount = 0;\n      final List<TypeAnnotation> typeAnnotations = TypeAnnotation.listFrom(mt);\n      if (!clInit && !dInit) {\n        if (descriptor != null && !descriptor.typeParameters.isEmpty()) {\n          appendTypeParameters(buffer, descriptor.typeParameters, descriptor.typeParameterBounds, typeAnnotations);\n          buffer.append(' ');\n        }\n\n        final List<TypeAnnotation> emptyTypeAnnotations = TargetInfo.EmptyTarget.extract(typeAnnotations);\n        if (init) {\n          emptyTypeAnnotations.forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));\n        } else {\n          if (descriptor != null) {\n            buffer.append(GenericMain.getGenericCastTypeName(descriptor.returnType, TypeAnnotationWriteHelper.create(emptyTypeAnnotations)));\n          }\n          else {\n            buffer.append(ExprProcessor.getCastTypeName(md.ret, TypeAnnotationWriteHelper.create(emptyTypeAnnotations)));\n          }\n          buffer.append(' ');\n        }\n\n        buffer.append(toValidJavaIdentifier(name));\n        buffer.append('(');\n\n        List<VarVersionPair> mask = methodWrapper.synthParameters;\n\n        int lastVisibleParameterIndex = -1;\n        for (int i = 0; i < md.params.length; i++) {\n          if (mask == null || mask.get(i) == null) {\n            lastVisibleParameterIndex = i;\n          }\n        }\n\n        int index = methodWrapper.varproc.getFirstParameterVarIndex();\n        for (int i = methodWrapper.varproc.getFirstParameterPosition(); i < md.params.length; i++) {\n          if (mask == null || mask.get(i) == null) {\n            if (paramCount > 0) {\n              buffer.append(\", \");\n            }\n\n            Type paramType;\n            if (descriptor != null) paramType = descriptor.parameterTypes.get(paramCount); else paramType = md.params[i];\n            appendParameterAnnotations(buffer, mt, paramType, paramCount);\n\n            VarVersionPair pair = new VarVersionPair(index, 0);\n            if (methodWrapper.varproc.isParameterFinal(pair) ||\n                methodWrapper.varproc.getVarFinal(pair) == VarProcessor.VAR_EXPLICIT_FINAL) {\n              buffer.append(\"final \");\n            }\n\n            String typeName;\n            boolean isVarArg = i == lastVisibleParameterIndex && mt.hasModifier(CodeConstants.ACC_VARARGS);\n            List<TypeAnnotation> typeParamAnnotations = TargetInfo.FormalParameterTarget.extract(typeAnnotations, i);\n            if (paramType instanceof GenericType genParamType) {\n              isVarArg &= genParamType.getArrayDim() > 0;\n              if (isVarArg) {\n                genParamType = genParamType.decreaseArrayDim();\n              }\n              typeName = GenericMain.getGenericCastTypeName(genParamType, TypeAnnotationWriteHelper.create(typeParamAnnotations));\n            }\n            else {\n              VarType varParamType = (VarType) paramType;\n              isVarArg &= varParamType.getArrayDim() > 0;\n              if (isVarArg) {\n                varParamType = varParamType.decreaseArrayDim();\n              }\n              typeName = ExprProcessor.getCastTypeName(varParamType, TypeAnnotationWriteHelper.create(typeParamAnnotations));\n            }\n\n            if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&\n                DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {\n              typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, TypeAnnotationWriteHelper.create(typeParamAnnotations));\n            }\n            buffer.append(typeName);\n            if (isVarArg) {\n              buffer.append(\"...\");\n            }\n\n            buffer.append(' ');\n\n            String parameterName = methodWrapper.varproc.getVarName(pair);\n            buffer.append(parameterName == null ? \"param\" + index : parameterName); // null iff decompiled with errors\n\n            paramCount++;\n          }\n\n          index += md.params[i].getStackSize();\n        }\n\n        buffer.append(')');\n\n        StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);\n        if ((descriptor != null && !descriptor.exceptionTypes.isEmpty()) || attr != null) {\n          throwsExceptions = true;\n          buffer.append(\" throws \");\n\n          for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {\n            if (i > 0) {\n              buffer.append(\", \");\n            }\n            TargetInfo.ThrowsTarget.extract(typeAnnotations, i).forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));\n            if (descriptor != null && !descriptor.exceptionTypes.isEmpty()) {\n              GenericType type = descriptor.exceptionTypes.get(i);\n              buffer.append(GenericMain.getGenericCastTypeName(type, Collections.emptyList()));\n            }\n            else {\n              VarType type = new VarType(attr.getExcClassname(i, cl.getPool()), true);\n              buffer.append(ExprProcessor.getCastTypeName(type, Collections.emptyList()));\n            }\n          }\n        }\n      }\n\n      tracer.incrementCurrentSourceLine(buffer.countLines(start_index_method));\n\n      if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { // native or abstract method (explicit or interface)\n        if (isAnnotation) {\n          StructAnnDefaultAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_ANNOTATION_DEFAULT);\n          if (attr != null) {\n            buffer.append(\" default \");\n            buffer.append(attr.getDefaultValue().toJava(0, BytecodeMappingTracer.DUMMY));\n          }\n        }\n\n        buffer.append(';');\n        buffer.appendLineSeparator();\n      }\n      else {\n        if (!clInit && !dInit) {\n          buffer.append(' ');\n        }\n\n        // We do not have line information for method start, lets have it here for now\n        buffer.append('{').appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n\n        RootStatement root = wrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;\n\n        if (root != null && !methodWrapper.decompiledWithErrors) { // check for existence\n          try {\n            // to restore in case of an exception\n            BytecodeMappingTracer codeTracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine());\n            TextBuffer code = root.toJava(indent + 1, codeTracer);\n\n            hideMethod = code.length() == 0 &&\n              (clInit || dInit || hideConstructor(node, !typeAnnotations.isEmpty(), init, throwsExceptions, paramCount, flags)) ||\n              isSyntheticRecordMethod(cl, mt, code);\n\n            buffer.append(code);\n\n            tracer.setCurrentSourceLine(codeTracer.getCurrentSourceLine());\n            tracer.addTracer(codeTracer);\n          }\n          catch (Throwable t) {\n            String message = \"Method \" + mt.getName() + \" \" + mt.getDescriptor() + \" couldn't be written.\";\n            DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);\n            methodWrapper.decompiledWithErrors = true;\n          }\n        }\n\n        if (methodWrapper.decompiledWithErrors) {\n          buffer.appendIndent(indent + 1);\n          buffer.append(\"// $FF: Couldn't be decompiled\");\n          buffer.appendLineSeparator();\n          tracer.incrementCurrentSourceLine();\n        }\n        else if (root != null) {\n          tracer.addMapping(root.getDummyExit().bytecode);\n        }\n        buffer.appendIndent(indent).append('}').appendLineSeparator();\n      }\n\n      tracer.incrementCurrentSourceLine();\n    }\n    finally {\n      DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);\n    }\n\n    // save total lines\n    // TODO: optimize\n    //tracer.setCurrentSourceLine(buffer.countLines(start_index_method));\n\n    return !hideMethod;\n  }\n\n  private static boolean isVarArgRecord(StructClass cl) {\n    String canonicalConstructorDescriptor =\n      cl.getRecordComponents().stream().map(StructField::getDescriptor).collect(Collectors.joining(\"\", \"(\", \")V\"));\n    StructMethod init = cl.getMethod(CodeConstants.INIT_NAME, canonicalConstructorDescriptor);\n    return init != null && init.hasModifier(CodeConstants.ACC_VARARGS);\n  }\n\n  public static void packageInfoToJava(StructClass cl, TextBuffer buffer) {\n    appendAnnotations(buffer, 0, cl);\n\n    int index = cl.qualifiedName.lastIndexOf('/');\n    String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');\n    buffer.append(\"package \").append(packageName).append(';').appendLineSeparator().appendLineSeparator();\n  }\n\n  public static void moduleInfoToJava(StructClass cl, TextBuffer buffer) {\n    appendAnnotations(buffer, 0, cl);\n\n    StructModuleAttribute moduleAttribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);\n\n    if ((moduleAttribute.moduleFlags & CodeConstants.ACC_OPEN) != 0) {\n      buffer.append(\"open \");\n    }\n\n    buffer.append(\"module \").append(moduleAttribute.moduleName).append(\" {\").appendLineSeparator();\n\n    writeModuleInfoBody(buffer, moduleAttribute);\n\n    buffer.append('}').appendLineSeparator();\n  }\n\n  private static void methodLambdaToJava(ClassNode lambdaNode,\n                                         ClassWrapper classWrapper,\n                                         StructMethod mt,\n                                         TextBuffer buffer,\n                                         int indent,\n                                         boolean codeOnly, BytecodeMappingTracer tracer) {\n    MethodWrapper methodWrapper = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor());\n\n    MethodWrapper outerWrapper = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);\n    DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, methodWrapper);\n\n    try {\n      String method_name = lambdaNode.lambdaInformation.method_name;\n      MethodDescriptor md_content = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.content_method_descriptor);\n      MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(lambdaNode.lambdaInformation.method_descriptor);\n\n      if (!codeOnly) {\n        buffer.appendIndent(indent);\n        buffer.append(\"public \");\n        buffer.append(method_name);\n        buffer.append(\"(\");\n\n        boolean firstParameter = true;\n        int index = lambdaNode.lambdaInformation.is_content_method_static ? 0 : 1;\n        int start_index = md_content.params.length - md_lambda.params.length;\n\n        for (int i = 0; i < md_content.params.length; i++) {\n          if (i >= start_index) {\n            if (!firstParameter) {\n              buffer.append(\", \");\n            }\n\n            String typeName = ExprProcessor.getCastTypeName(md_content.params[i].copy(), Collections.emptyList());\n            if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeName) &&\n                DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {\n              typeName = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, Collections.emptyList());\n            }\n\n            buffer.append(typeName);\n            buffer.append(\" \");\n\n            String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0));\n            buffer.append(parameterName == null ? \"param\" + index : parameterName); // null iff decompiled with errors\n\n            firstParameter = false;\n          }\n\n          index += md_content.params[i].getStackSize();\n        }\n\n        buffer.append(\") {\").appendLineSeparator();\n\n        indent += 1;\n      }\n\n      RootStatement root = classWrapper.getMethodWrapper(mt.getName(), mt.getDescriptor()).root;\n      if (!methodWrapper.decompiledWithErrors) {\n        if (root != null) { // check for existence\n          try {\n            buffer.append(root.toJava(indent, tracer));\n          }\n          catch (Throwable t) {\n            String message = \"Method \" + mt.getName() + \" \" + mt.getDescriptor() + \" couldn't be written.\";\n            DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);\n            methodWrapper.decompiledWithErrors = true;\n          }\n        }\n      }\n\n      if (methodWrapper.decompiledWithErrors) {\n        buffer.appendIndent(indent);\n        buffer.append(\"// $FF: Couldn't be decompiled\");\n        buffer.appendLineSeparator();\n      }\n\n      if (root != null) {\n        tracer.addMapping(root.getDummyExit().bytecode);\n      }\n\n      if (!codeOnly) {\n        indent -= 1;\n        buffer.appendIndent(indent).append('}').appendLineSeparator();\n      }\n    }\n    finally {\n      DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);\n    }\n  }\n\n  private static String toValidJavaIdentifier(String name) {\n    if (name == null || name.isEmpty()) return name;\n\n    boolean changed = false;\n    StringBuilder res = new StringBuilder(name.length());\n    for (int i = 0; i < name.length(); i++) {\n      char c = name.charAt(i);\n      if ((i == 0 && !Character.isJavaIdentifierStart(c))\n          || (i > 0 && !Character.isJavaIdentifierPart(c))) {\n        changed = true;\n        res.append(\"_\");\n      }\n      else {\n        res.append(c);\n      }\n    }\n    if (!changed) {\n      return name;\n    }\n    return res.append(\"/* $FF was: \").append(name).append(\"*/\").toString();\n  }\n\n  private static void recordComponentToJava(StructRecordComponent cd, TextBuffer buffer, boolean varArgComponent) {\n    Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(cd);\n    VarType fieldType = fieldTypeData.getKey();\n    GenericFieldDescriptor descriptor = fieldTypeData.getValue();\n\n    appendAnnotations(buffer, -1, cd);\n\n    final List<TypeAnnotation> typeAnnotations = TypeAnnotation.listFrom(cd);\n    if (descriptor != null) {\n      buffer.append(GenericMain.getGenericCastTypeName(\n        varArgComponent ? descriptor.type.decreaseArrayDim() : descriptor.type,\n        TypeAnnotationWriteHelper.create(typeAnnotations)\n      ));\n    }\n    else {\n      buffer.append(ExprProcessor.getCastTypeName(\n        varArgComponent ? fieldType.decreaseArrayDim() : fieldType,\n        TypeAnnotationWriteHelper.create(typeAnnotations)\n      ));\n    }\n    if (varArgComponent) {\n      buffer.append(\"...\");\n    }\n    buffer.append(' ');\n\n    buffer.append(cd.getName());\n  }\n\n  private static boolean hideConstructor(\n    ClassNode node,\n    boolean hasAnnotation,\n    boolean init,\n    boolean throwsExceptions,\n    int paramCount,\n    int methodAccessFlags\n  ) {\n    if (!init || hasAnnotation|| throwsExceptions || paramCount > 0 || !DecompilerContext.getOption(IFernflowerPreferences.HIDE_DEFAULT_CONSTRUCTOR)) {\n      return false;\n    }\n\n    StructClass cl = node.getWrapper().getClassStruct();\n\n\t  int classAccessFlags = node.type == ClassNode.CLASS_ROOT ? cl.getAccessFlags() : node.access;\n    boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);\n\n    // default constructor requires same accessibility flags. Exception: enum constructor which is always private\n    if (!isEnum && ((classAccessFlags & ACCESSIBILITY_FLAGS) != (methodAccessFlags & ACCESSIBILITY_FLAGS))) {\n  \t  return false;\n  \t}\n\n    int count = 0;\n    for (StructMethod mt : cl.getMethods()) {\n      if (CodeConstants.INIT_NAME.equals(mt.getName()) && ++count > 1) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  private static Map.Entry<VarType, GenericFieldDescriptor> getFieldTypeData(StructField fd) {\n    VarType fieldType = new VarType(fd.getDescriptor(), false);\n\n    GenericFieldDescriptor descriptor = null;\n    if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {\n      StructGenericSignatureAttribute attr = fd.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);\n      if (attr != null) {\n        descriptor = GenericMain.parseFieldSignature(attr.getSignature());\n      }\n    }\n\n    return new AbstractMap.SimpleImmutableEntry<>(fieldType, descriptor);\n  }\n\n  private static void appendDeprecation(TextBuffer buffer, int indent) {\n    buffer.appendIndent(indent).append(\"/** @deprecated */\").appendLineSeparator();\n  }\n\n  private enum MType {CLASS, FIELD, METHOD}\n\n  private static void appendRenameComment(TextBuffer buffer, String oldName, MType type, int indent) {\n    if (oldName == null) return;\n\n    buffer.appendIndent(indent);\n    buffer.append(\"// $FF: renamed from: \");\n\n    switch (type) {\n      case CLASS -> buffer.append(ExprProcessor.buildJavaClassName(oldName));\n      case FIELD -> {\n        String[] fParts = oldName.split(\" \");\n        FieldDescriptor fd = FieldDescriptor.parseDescriptor(fParts[2]);\n        buffer.append(fParts[1]);\n        buffer.append(' ');\n        buffer.append(getTypePrintOut(fd.type));\n      }\n      default -> {\n        String[] mParts = oldName.split(\" \");\n        MethodDescriptor md = MethodDescriptor.parseDescriptor(mParts[2]);\n        buffer.append(mParts[1]);\n        buffer.append(\" (\");\n        boolean first = true;\n        for (VarType paramType : md.params) {\n          if (!first) {\n            buffer.append(\", \");\n          }\n          first = false;\n          buffer.append(getTypePrintOut(paramType));\n        }\n        buffer.append(\") \");\n        buffer.append(getTypePrintOut(md.ret));\n      }\n    }\n\n    buffer.appendLineSeparator();\n  }\n\n  private static String getTypePrintOut(VarType type) {\n    String typeText = ExprProcessor.getCastTypeName(type, false, Collections.emptyList());\n    if (ExprProcessor.UNDEFINED_TYPE_STRING.equals(typeText) &&\n        DecompilerContext.getOption(IFernflowerPreferences.UNDEFINED_PARAM_TYPE_OBJECT)) {\n      typeText = ExprProcessor.getCastTypeName(VarType.VARTYPE_OBJECT, false, Collections.emptyList());\n    }\n    return typeText;\n  }\n\n  private static void appendComment(TextBuffer buffer, String comment, int indent) {\n    buffer.appendIndent(indent).append(\"// $FF: \").append(comment).appendLineSeparator();\n  }\n\n  private static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb) {\n    for (StructGeneralAttribute.Key<?> key : StructGeneralAttribute.ANNOTATION_ATTRIBUTES) {\n      StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttribute(key);\n      if (attribute != null) {\n        for (AnnotationExprent annotation : attribute.getAnnotations()) {\n          if (mb.memberAnnCollidesWithTypeAnnotation(annotation)) continue;\n          String text = annotation.toJava(indent, BytecodeMappingTracer.DUMMY).toString();\n          buffer.append(text);\n          if (indent < 0) {\n            buffer.append(' ');\n          }\n          else {\n            buffer.appendLineSeparator();\n          }\n        }\n      }\n    }\n  }\n\n  private static void appendParameterAnnotations(TextBuffer buffer, StructMethod mt, @NotNull Type type, int param) {\n    for (StructGeneralAttribute.Key<?> key : StructGeneralAttribute.PARAMETER_ANNOTATION_ATTRIBUTES) {\n      StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttribute(key);\n      if (attribute != null) {\n        List<List<AnnotationExprent>> annotations = attribute.getParamAnnotations();\n        if (param < annotations.size()) {\n          for (AnnotationExprent annotation : annotations.get(param)) {\n            if (mt.paramAnnCollidesWithTypeAnnotation(annotation, type, param)) continue;\n            String text = annotation.toJava(-1, BytecodeMappingTracer.DUMMY).toString();\n            buffer.append(text).append(' ');\n          }\n        }\n      }\n    }\n  }\n\n  private static final Map<Integer, String> MODIFIERS;\n  static {\n    MODIFIERS = new LinkedHashMap<>();\n    MODIFIERS.put(CodeConstants.ACC_PUBLIC, \"public\");\n    MODIFIERS.put(CodeConstants.ACC_PROTECTED, \"protected\");\n    MODIFIERS.put(CodeConstants.ACC_PRIVATE, \"private\");\n    MODIFIERS.put(CodeConstants.ACC_ABSTRACT, \"abstract\");\n    MODIFIERS.put(CodeConstants.ACC_STATIC, \"static\");\n    MODIFIERS.put(CodeConstants.ACC_FINAL, \"final\");\n    MODIFIERS.put(CodeConstants.ACC_STRICT, \"strictfp\");\n    MODIFIERS.put(CodeConstants.ACC_TRANSIENT, \"transient\");\n    MODIFIERS.put(CodeConstants.ACC_VOLATILE, \"volatile\");\n    MODIFIERS.put(CodeConstants.ACC_SYNCHRONIZED, \"synchronized\");\n    MODIFIERS.put(CodeConstants.ACC_NATIVE, \"native\");\n  }\n\n  private static final int CLASS_ALLOWED =\n    CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT |\n    CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_STRICT;\n  private static final int FIELD_ALLOWED =\n    CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_STATIC |\n    CodeConstants.ACC_FINAL | CodeConstants.ACC_TRANSIENT | CodeConstants.ACC_VOLATILE;\n  private static final int METHOD_ALLOWED =\n    CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE | CodeConstants.ACC_ABSTRACT |\n    CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNCHRONIZED | CodeConstants.ACC_NATIVE |\n    CodeConstants.ACC_STRICT;\n\n  private static final int CLASS_EXCLUDED = CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_STATIC;\n  private static final int FIELD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL;\n  private static final int METHOD_EXCLUDED = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_ABSTRACT;\n\n  private static final int ACCESSIBILITY_FLAGS = CodeConstants.ACC_PUBLIC | CodeConstants.ACC_PROTECTED | CodeConstants.ACC_PRIVATE;\n\n  private static void appendModifiers(TextBuffer buffer, int flags, int allowed, boolean isInterface, int excluded) {\n    flags &= allowed;\n    if (!isInterface) excluded = 0;\n    for (int modifier : MODIFIERS.keySet()) {\n      if ((flags & modifier) == modifier && (modifier & excluded) == 0) {\n        buffer.append(MODIFIERS.get(modifier)).append(' ');\n      }\n    }\n  }\n\n  public static GenericClassDescriptor getGenericClassDescriptor(StructClass cl) {\n    if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {\n      StructGenericSignatureAttribute attr = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_SIGNATURE);\n      if (attr != null) {\n        return GenericMain.parseClassSignature(attr.getSignature());\n      }\n    }\n    return null;\n  }\n\n  public static void appendTypeParameters(\n    TextBuffer buffer,\n    List<String> parameters,\n    List<? extends List<GenericType>> bounds,\n    final List<TypeAnnotation> typeAnnotations\n  ) {\n    buffer.append('<');\n    for (int i = 0; i < parameters.size(); i++) {\n      if (i > 0) {\n        buffer.append(\", \");\n      }\n      TargetInfo.TypeParameterTarget.extract(typeAnnotations, i).forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));\n      buffer.append(parameters.get(i));\n      List<GenericType> parameterBounds = bounds.get(i);\n      if (parameterBounds.size() > 1 || !\"java/lang/Object\".equals(parameterBounds.get(0).getValue())) {\n        buffer.append(\" extends \");\n        TargetInfo.TypeParameterBoundTarget.extract(typeAnnotations, i, 0).forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));\n        buffer.append(GenericMain.getGenericCastTypeName(parameterBounds.get(0), Collections.emptyList()));\n        for (int j = 1; j < parameterBounds.size(); j++) {\n          buffer.append(\" & \");\n          TargetInfo.TypeParameterBoundTarget.extract(typeAnnotations, i, j).forEach(typeAnnotation -> typeAnnotation.writeTo(buffer));\n          buffer.append(GenericMain.getGenericCastTypeName(parameterBounds.get(j), Collections.emptyList()));\n        }\n      }\n    }\n\n    buffer.append('>');\n  }\n\n  private static void appendFQClassNames(TextBuffer buffer, List<String> names) {\n    for (int i = 0; i < names.size(); i++) {\n      String name = names.get(i);\n      buffer.appendIndent(2).append(name);\n      if (i < names.size() - 1) {\n        buffer.append(',').appendLineSeparator();\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/ClassesProcessor.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper;\nimport org.jetbrains.java.decompiler.main.collectors.ImportCollector;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.main.rels.LambdaProcessor;\nimport org.jetbrains.java.decompiler.main.rels.NestedClassProcessor;\nimport org.jetbrains.java.decompiler.main.rels.NestedMemberAccess;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructContext;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class ClassesProcessor {\n  public static final int AVERAGE_CLASS_SIZE = 16 * 1024;\n\n  private final StructContext context;\n  private final Map<String, ClassNode> mapRootClasses = new HashMap<>();\n\n  private static class Inner {\n    private String simpleName;\n    private int type;\n    private int accessFlags;\n\n    private static boolean equal(Inner o1, Inner o2) {\n      return o1.type == o2.type && o1.accessFlags == o2.accessFlags && Objects.equals(o1.simpleName, o2.simpleName);\n    }\n  }\n\n  public ClassesProcessor(StructContext context) {\n    this.context = context;\n  }\n\n  public void loadClasses(IIdentifierRenamer renamer) {\n    Map<String, Inner> mapInnerClasses = new HashMap<>();\n    Map<String, Set<String>> mapNestedClassReferences = new HashMap<>();\n    Map<String, Set<String>> mapEnclosingClassReferences = new HashMap<>();\n    Map<String, String> mapNewSimpleNames = new HashMap<>();\n\n    boolean bDecompileInner = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_INNER);\n    boolean verifyAnonymousClasses = DecompilerContext.getOption(IFernflowerPreferences.VERIFY_ANONYMOUS_CLASSES);\n\n    // create class nodes\n    for (StructClass cl : context.getClasses().values()) {\n      if (cl.isOwn() && !mapRootClasses.containsKey(cl.qualifiedName)) {\n        if (bDecompileInner) {\n          StructInnerClassesAttribute inner = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);\n\n          if (inner != null) {\n            for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) {\n              String innerName = entry.innerName;\n\n              // original simple name\n              String simpleName = entry.simpleName;\n              String savedName = mapNewSimpleNames.get(innerName);\n              if (savedName != null) {\n                simpleName = savedName;\n              }\n              else if (simpleName != null &&\n                       renamer != null &&\n                       renamer.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, simpleName, null, null)) {\n                simpleName = renamer.getNextClassName(innerName, simpleName);\n                mapNewSimpleNames.put(innerName, simpleName);\n              }\n\n              Inner rec = new Inner();\n              rec.simpleName = simpleName;\n              rec.type = entry.simpleNameIdx == 0 ? ClassNode.CLASS_ANONYMOUS : entry.outerNameIdx == 0 ? ClassNode.CLASS_LOCAL : ClassNode.CLASS_MEMBER;\n              rec.accessFlags = entry.accessFlags;\n\n              // enclosing class\n              String enclClassName = entry.outerNameIdx != 0 ? entry.enclosingName : cl.qualifiedName;\n              if (enclClassName == null || innerName.equals(enclClassName)) {\n                continue;  // invalid name or self reference\n              }\n              if (rec.type == ClassNode.CLASS_MEMBER && !innerName.equals(enclClassName + '$' + entry.simpleName)) {\n                continue;  // not a real inner class\n              }\n\n              StructClass enclosingClass = context.getClasses().get(enclClassName);\n              if (enclosingClass != null && enclosingClass.isOwn()) { // own classes only\n                Inner existingRec = mapInnerClasses.get(innerName);\n                if (existingRec == null) {\n                  mapInnerClasses.put(innerName, rec);\n                }\n                else if (!Inner.equal(existingRec, rec)) {\n                  String message = \"Inconsistent inner class entries for \" + innerName + \"!\";\n                  DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n                }\n\n                // reference to the nested class\n                mapNestedClassReferences.computeIfAbsent(enclClassName, k -> new HashSet<>()).add(innerName);\n                // reference to the enclosing class\n                mapEnclosingClassReferences.computeIfAbsent(innerName, k -> new HashSet<>()).add(enclClassName);\n              }\n            }\n          }\n        }\n\n        ClassNode node = new ClassNode(ClassNode.CLASS_ROOT, cl);\n        node.access = cl.getAccessFlags();\n        mapRootClasses.put(cl.qualifiedName, node);\n      }\n    }\n\n    // set non-sealed if class extends or implements a sealed class and is not final or sealed itself\n    for (ClassNode clazz : mapRootClasses.values()) {\n      if (clazz.classStruct.hasSealedClassesSupport() &&\n          (clazz.access & CodeConstants.ACC_FINAL) == 0 &&\n          clazz.classStruct.getPermittedSubclasses() == null) {\n        List<String> qualifiedSealedSuperNames = new ArrayList<>(Arrays.asList(clazz.classStruct.getInterfaceNames()));\n        PrimitiveConstant superConst = clazz.classStruct.superClass;\n        if (superConst != null) qualifiedSealedSuperNames.add(superConst.getString());\n        clazz.setNonSealed(\n          qualifiedSealedSuperNames.stream()\n            .map(mapRootClasses::get)\n            .filter(Objects::nonNull)\n            .map(potentialSuper -> potentialSuper.classStruct.getPermittedSubclasses())\n            .filter(Objects::nonNull)\n            .anyMatch(permittedList -> permittedList.contains(clazz.classStruct.qualifiedName))\n        );\n      }\n    }\n\n    if (bDecompileInner) {\n      // connect nested classes\n      for (Entry<String, ClassNode> ent : mapRootClasses.entrySet()) {\n        // root class?\n        if (!mapInnerClasses.containsKey(ent.getKey())) {\n          Set<String> setVisited = new HashSet<>();\n          LinkedList<String> stack = new LinkedList<>();\n\n          stack.add(ent.getKey());\n          setVisited.add(ent.getKey());\n\n          while (!stack.isEmpty()) {\n            String superClass = stack.removeFirst();\n            ClassNode superNode = mapRootClasses.get(superClass);\n\n            Set<String> setNestedClasses = mapNestedClassReferences.get(superClass);\n            if (setNestedClasses != null) {\n              StructClass scl = superNode.classStruct;\n              StructInnerClassesAttribute inner = scl.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);\n\n              if (inner == null || inner.getEntries().isEmpty()) {\n                DecompilerContext.getLogger().writeMessage(superClass + \" does not contain inner classes!\", IFernflowerLogger.Severity.WARN);\n                continue;\n              }\n\n              for (StructInnerClassesAttribute.Entry entry : inner.getEntries()) {\n                String nestedClass = entry.innerName;\n                if (!setNestedClasses.contains(nestedClass)) {\n                  continue;\n                }\n\n                if (!setVisited.add(nestedClass)) {\n                  continue;\n                }\n\n                ClassNode nestedNode = mapRootClasses.get(nestedClass);\n                if (nestedNode == null) {\n                  DecompilerContext.getLogger().writeMessage(\"Nested class \" + nestedClass + \" missing!\", IFernflowerLogger.Severity.WARN);\n                  continue;\n                }\n\n                Inner rec = mapInnerClasses.get(nestedClass);\n\n                //if ((Integer)arr[2] == ClassNode.CLASS_MEMBER) {\n                  // FIXME: check for consistent naming\n                //}\n\n                nestedNode.simpleName = rec.simpleName;\n                nestedNode.type = rec.type;\n                nestedNode.access = rec.accessFlags;\n\n                // sanity checks of the class supposed to be anonymous\n                if (verifyAnonymousClasses && nestedNode.type == ClassNode.CLASS_ANONYMOUS && !isAnonymous(nestedNode.classStruct, scl)) {\n                  nestedNode.type = ClassNode.CLASS_LOCAL;\n                }\n\n                if (nestedNode.type == ClassNode.CLASS_ANONYMOUS) {\n                  StructClass cl = nestedNode.classStruct;\n                  // remove static if anonymous class (a common compiler bug)\n                  nestedNode.access &= ~CodeConstants.ACC_STATIC;\n\n                  int[] interfaces = cl.getInterfaces();\n                  if (interfaces.length > 0) {\n                    nestedNode.anonymousClassType = new VarType(cl.getInterface(0), true);\n                  }\n                  else {\n                    nestedNode.anonymousClassType = new VarType(cl.superClass.getString(), true);\n                  }\n                }\n                else if (nestedNode.type == ClassNode.CLASS_LOCAL) {\n                  // only abstract and final are permitted (a common compiler bug)\n                  nestedNode.access &= (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_FINAL);\n                }\n\n                superNode.nested.add(nestedNode);\n                nestedNode.parent = superNode;\n\n                nestedNode.enclosingClasses.addAll(mapEnclosingClassReferences.get(nestedClass));\n\n                stack.add(nestedClass);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  private static boolean isAnonymous(StructClass cl, StructClass enclosingCl) {\n    // checking super class and interfaces\n    int[] interfaces = cl.getInterfaces();\n    if (interfaces.length > 0) {\n      boolean hasNonTrivialSuperClass = cl.superClass != null && !VarType.VARTYPE_OBJECT.equals(new VarType(cl.superClass.getString(), true));\n      if (hasNonTrivialSuperClass || interfaces.length > 1) { // can't have multiple 'sources'\n        String message = \"Inconsistent anonymous class definition: '\" + cl.qualifiedName + \"'. Multiple interfaces and/or super class defined.\";\n        DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n        return false;\n      }\n    }\n    else if (cl.superClass == null) { // neither interface nor super class defined\n      String message = \"Inconsistent anonymous class definition: '\" + cl.qualifiedName + \"'. Neither interface nor super class defined.\";\n      DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n      return false;\n    }\n\n    // FIXME: check constructors\n    // FIXME: check enclosing class/method\n\n    ConstantPool pool = enclosingCl.getPool();\n\n    int refCounter = 0;\n    boolean refNotNew = false;\n\n    StructEnclosingMethodAttribute attribute = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_ENCLOSING_METHOD);\n    String enclosingMethod = attribute != null ? attribute.getMethodName() : null;\n\n    // checking references in the enclosing class\n    for (StructMethod mt : enclosingCl.getMethods()) {\n      if (enclosingMethod != null && !enclosingMethod.equals(mt.getName())) {\n        continue;\n      }\n\n      try {\n        mt.expandData(enclosingCl);\n\n        InstructionSequence seq = mt.getInstructionSequence();\n        if (seq != null) {\n          int len = seq.length();\n          for (int i = 0; i < len; i++) {\n            Instruction instr = seq.getInstr(i);\n            switch (instr.opcode) {\n              case CodeConstants.opc_checkcast, CodeConstants.opc_instanceof -> {\n                if (cl.qualifiedName.equals(pool.getPrimitiveConstant(instr.operand(0)).getString())) {\n                  refCounter++;\n                  refNotNew = true;\n                }\n              }\n              case CodeConstants.opc_new, CodeConstants.opc_anewarray, CodeConstants.opc_multianewarray -> {\n                if (cl.qualifiedName.equals(pool.getPrimitiveConstant(instr.operand(0)).getString())) {\n                  refCounter++;\n                }\n              }\n              case CodeConstants.opc_getstatic, CodeConstants.opc_putstatic -> {\n                if (cl.qualifiedName.equals(pool.getLinkConstant(instr.operand(0)).className)) {\n                  refCounter++;\n                  refNotNew = true;\n                }\n              }\n            }\n          }\n        }\n\n        mt.releaseResources();\n      }\n      catch (IOException ex) {\n        String message = \"Could not read method while checking anonymous class definition: '\" + enclosingCl.qualifiedName + \"', '\" +\n                         InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()) + \"'\";\n        DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n        return false;\n      }\n\n      if (refCounter > 1 || refNotNew) {\n        String message = \"Inconsistent references to the class '\" + cl.qualifiedName + \"' which is supposed to be anonymous\";\n        DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  public void writeClass(StructClass cl, TextBuffer buffer) throws IOException {\n    ClassNode root = mapRootClasses.get(cl.qualifiedName);\n    if (root.type != ClassNode.CLASS_ROOT) {\n      return;\n    }\n\n    boolean packageInfo = cl.isSynthetic() && \"package-info\".equals(root.simpleName);\n    boolean moduleInfo = cl.hasModifier(CodeConstants.ACC_MODULE) && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_MODULE);\n\n    DecompilerContext.getLogger().startReadingClass(cl.qualifiedName);\n    try {\n      ImportCollector importCollector = new ImportCollector(root);\n      DecompilerContext.startClass(importCollector);\n\n      if (packageInfo) {\n        ClassWriter.packageInfoToJava(cl, buffer);\n\n        importCollector.writeImports(buffer, false);\n      }\n      else if (moduleInfo) {\n        TextBuffer moduleBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);\n        ClassWriter.moduleInfoToJava(cl, moduleBuffer);\n\n        importCollector.writeImports(buffer, true);\n\n        buffer.append(moduleBuffer);\n      }\n      else {\n        new LambdaProcessor().processClass(root);\n\n        // add simple class names to implicit import\n        addClassNameToImport(root, importCollector);\n\n        // build wrappers for all nested classes (that's where actual processing takes place)\n        initWrappers(root);\n\n        new NestedClassProcessor().processClass(root, root);\n\n        new NestedMemberAccess().propagateMemberAccess(root);\n\n        TextBuffer classBuffer = new TextBuffer(AVERAGE_CLASS_SIZE);\n        new ClassWriter().classToJava(root, classBuffer, 0, null);\n\n        int index = cl.qualifiedName.lastIndexOf('/');\n        if (index >= 0) {\n          String packageName = cl.qualifiedName.substring(0, index).replace('/', '.');\n          buffer.append(\"package \").append(packageName).append(';').appendLineSeparator().appendLineSeparator();\n        }\n\n        importCollector.writeImports(buffer, true);\n\n        int offsetLines = buffer.countLines();\n\n        buffer.append(classBuffer);\n\n        if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {\n          BytecodeSourceMapper mapper = DecompilerContext.getBytecodeSourceMapper();\n          mapper.addTotalOffset(offsetLines);\n          if (DecompilerContext.getOption(IFernflowerPreferences.DUMP_ORIGINAL_LINES)) {\n            buffer.dumpOriginalLineNumbers(mapper.getOriginalLinesMapping());\n          }\n          if (DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE)) {\n            buffer.appendLineSeparator();\n            mapper.dumpMapping(buffer, true);\n          }\n        }\n      }\n    }\n    finally {\n      destroyWrappers(root);\n      DecompilerContext.getLogger().endReadingClass();\n    }\n  }\n\n  private static void initWrappers(ClassNode node) {\n    if (node.type == ClassNode.CLASS_LAMBDA) {\n      return;\n    }\n\n    ClassWrapper wrapper = new ClassWrapper(node.classStruct);\n    wrapper.init();\n\n    node.wrapper = wrapper;\n\n    for (ClassNode nd : node.nested) {\n      initWrappers(nd);\n    }\n  }\n\n  private static void addClassNameToImport(ClassNode node, ImportCollector imp) {\n    if (node.simpleName != null && node.simpleName.length() > 0) {\n      imp.getNestedName(node.type == ClassNode.CLASS_ROOT ? node.classStruct.qualifiedName : node.simpleName, false);\n    }\n\n    for (ClassNode nd : node.nested) {\n      addClassNameToImport(nd, imp);\n    }\n  }\n\n  private static void destroyWrappers(ClassNode node) {\n    node.wrapper = null;\n    node.classStruct.releaseResources();\n\n    for (ClassNode nd : node.nested) {\n      destroyWrappers(nd);\n    }\n  }\n\n  public Map<String, ClassNode> getMapRootClasses() {\n    return mapRootClasses;\n  }\n\n\n  public static class ClassNode {\n    public static final int CLASS_ROOT = 0;\n    public static final int CLASS_MEMBER = 1;\n    public static final int CLASS_ANONYMOUS = 2;\n    public static final int CLASS_LOCAL = 4;\n    public static final int CLASS_LAMBDA = 8;\n\n    public int type;\n    public int access;\n    public boolean isNonSealed = false;\n    public String simpleName;\n    public final StructClass classStruct;\n    private ClassWrapper wrapper;\n    public String enclosingMethod;\n    public InvocationExprent superInvocation;\n    public final Map<String, VarVersionPair> mapFieldsToVars = new HashMap<>();\n    public VarType anonymousClassType;\n    public final List<ClassNode> nested = new ArrayList<>();\n    public final Set<String> enclosingClasses = new HashSet<>();\n    public ClassNode parent;\n    public LambdaInformation lambdaInformation;\n\n    public ClassNode(String content_class_name,\n                     String content_method_name,\n                     String content_method_descriptor,\n                     int content_method_invocation_type,\n                     String lambda_class_name,\n                     String lambda_method_name,\n                     String lambda_method_descriptor,\n                     StructClass classStruct) { // lambda class constructor\n      this.type = CLASS_LAMBDA;\n      this.classStruct = classStruct; // 'parent' class containing the static function\n\n      lambdaInformation = new LambdaInformation();\n\n      lambdaInformation.method_name = lambda_method_name;\n      lambdaInformation.method_descriptor = lambda_method_descriptor;\n\n      lambdaInformation.content_class_name = content_class_name;\n      lambdaInformation.content_method_name = content_method_name;\n      lambdaInformation.content_method_descriptor = content_method_descriptor;\n      lambdaInformation.content_method_invocation_type = content_method_invocation_type;\n\n      lambdaInformation.content_method_key =\n        InterpreterUtil.makeUniqueKey(lambdaInformation.content_method_name, lambdaInformation.content_method_descriptor);\n\n      anonymousClassType = new VarType(lambda_class_name, true);\n\n      boolean is_method_reference = !Objects.equals(content_class_name, classStruct.qualifiedName);\n      if (!is_method_reference) { // content method in the same class, check synthetic flag\n        StructMethod mt = classStruct.getMethod(content_method_name, content_method_descriptor);\n        is_method_reference = !mt.isSynthetic(); // if not synthetic -> method reference\n      }\n\n      lambdaInformation.is_method_reference = is_method_reference;\n      lambdaInformation.is_content_method_static =\n        (lambdaInformation.content_method_invocation_type == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic); // FIXME: redundant?\n    }\n\n    public ClassNode(int type, StructClass classStruct) {\n      this.type = type;\n      this.classStruct = classStruct;\n\n      simpleName = classStruct.qualifiedName.substring(classStruct.qualifiedName.lastIndexOf('/') + 1);\n    }\n\n    public ClassNode getClassNode(String qualifiedName) {\n      for (ClassNode node : nested) {\n        if (qualifiedName.equals(node.classStruct.qualifiedName)) {\n          return node;\n        }\n      }\n      return null;\n    }\n\n    public ClassWrapper getWrapper() {\n      ClassNode node = this;\n      while (node.type == CLASS_LAMBDA) {\n        node = node.parent;\n      }\n      return node.wrapper;\n    }\n\n    public boolean isNonSealed() {\n      return isNonSealed;\n    }\n\n    public void setNonSealed(boolean nonSealed) {\n      isNonSealed = nonSealed;\n    }\n\n    public static class LambdaInformation {\n      public String method_name;\n      public String method_descriptor;\n\n      public String content_class_name;\n      public String content_method_name;\n      public String content_method_descriptor;\n      public int content_method_invocation_type; // values from CONSTANT_MethodHandle_REF_*\n      public String content_method_key;\n\n      public boolean is_method_reference;\n      public boolean is_content_method_static;\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/DecompilerContext.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeSourceMapper;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.main.collectors.ImportCollector;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;\nimport org.jetbrains.java.decompiler.struct.StructContext;\n\nimport java.util.Map;\nimport java.util.Objects;\n\npublic class DecompilerContext {\n  public static final String CURRENT_CLASS = \"CURRENT_CLASS\";\n  public static final String CURRENT_CLASS_WRAPPER = \"CURRENT_CLASS_WRAPPER\";\n  public static final String CURRENT_CLASS_NODE = \"CURRENT_CLASS_NODE\";\n  public static final String CURRENT_METHOD_WRAPPER = \"CURRENT_METHOD_WRAPPER\";\n\n  @NotNull\n  private final Map<String, Object> properties;\n  @NotNull\n  private final IFernflowerLogger logger;\n  @NotNull\n  private final StructContext structContext;\n  @NotNull\n  private final ClassesProcessor classProcessor;\n  @Nullable\n  private final PoolInterceptor poolInterceptor;\n  @NotNull\n  private final CancellationManager cancellationManager;\n  private ImportCollector importCollector;\n  private VarProcessor varProcessor;\n  private CounterContainer counterContainer;\n  private BytecodeSourceMapper bytecodeSourceMapper;\n\n  public DecompilerContext(@NotNull Map<String, Object> properties,\n                           @NotNull IFernflowerLogger logger,\n                           @NotNull StructContext structContext,\n                           @NotNull ClassesProcessor classProcessor,\n                           @Nullable PoolInterceptor interceptor) {\n    this(properties, logger, structContext, classProcessor, interceptor, null);\n  }\n\n  public DecompilerContext(@NotNull Map<String, Object> properties,\n                           @NotNull IFernflowerLogger logger,\n                           @NotNull StructContext structContext,\n                           @NotNull ClassesProcessor classProcessor,\n                           @Nullable PoolInterceptor interceptor,\n                           @Nullable CancellationManager cancellationManager) {\n    Objects.requireNonNull(properties);\n    Objects.requireNonNull(logger);\n    Objects.requireNonNull(structContext);\n    Objects.requireNonNull(classProcessor);\n    if (cancellationManager == null) {\n      Object object = properties.get(IFernflowerPreferences.MAX_PROCESSING_METHOD);\n      object = object == null ? \"0\" : object;\n      cancellationManager = CancellationManager.getSimpleWithTimeout(Integer.parseInt(object.toString()));\n    }\n\n    this.properties = properties;\n    this.logger = logger;\n    this.structContext = structContext;\n    this.classProcessor = classProcessor;\n    this.poolInterceptor = interceptor;\n    this.counterContainer = new CounterContainer();\n    this.cancellationManager = cancellationManager;\n  }\n\n  // *****************************************************************************\n  // context setup and update\n  // *****************************************************************************\n\n  private static final ThreadLocal<DecompilerContext> currentContext = new ThreadLocal<>();\n\n  public static DecompilerContext getCurrentContext() {\n    return currentContext.get();\n  }\n\n  public static void setCurrentContext(DecompilerContext context) {\n    currentContext.set(context);\n  }\n\n  public static void setProperty(String key, Object value) {\n    getCurrentContext().properties.put(key, value);\n  }\n\n  public static void startClass(ImportCollector importCollector) {\n    DecompilerContext context = getCurrentContext();\n    context.importCollector = importCollector;\n    context.counterContainer = new CounterContainer();\n    context.bytecodeSourceMapper = new BytecodeSourceMapper();\n  }\n\n  public static void startMethod(VarProcessor varProcessor) {\n    DecompilerContext context = getCurrentContext();\n    context.varProcessor = varProcessor;\n    context.counterContainer = new CounterContainer();\n  }\n\n  // *****************************************************************************\n  // context access\n  // *****************************************************************************\n\n  public static Object getProperty(String key) {\n    return getCurrentContext().properties.get(key);\n  }\n\n  public static boolean getOption(String key) {\n    return \"1\".equals(getProperty(key));\n  }\n\n  public static String getNewLineSeparator() {\n    return getOption(IFernflowerPreferences.NEW_LINE_SEPARATOR) ?\n           IFernflowerPreferences.LINE_SEPARATOR_UNX : IFernflowerPreferences.LINE_SEPARATOR_WIN;\n  }\n\n  public static IFernflowerLogger getLogger() {\n    return getCurrentContext().logger;\n  }\n\n  public static StructContext getStructContext() {\n    return getCurrentContext().structContext;\n  }\n\n  public static ClassesProcessor getClassProcessor() {\n    return getCurrentContext().classProcessor;\n  }\n\n  public static CancellationManager getCancellationManager() {\n    return getCurrentContext().cancellationManager;\n  }\n\n  public static PoolInterceptor getPoolInterceptor() {\n    return getCurrentContext().poolInterceptor;\n  }\n\n  public static ImportCollector getImportCollector() {\n    return getCurrentContext().importCollector;\n  }\n\n  public static VarProcessor getVarProcessor() {\n    return getCurrentContext().varProcessor;\n  }\n\n  public static CounterContainer getCounterContainer() {\n    return getCurrentContext().counterContainer;\n  }\n\n  public static BytecodeSourceMapper getBytecodeSourceMapper() {\n    return getCurrentContext().bytecodeSourceMapper;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/EnumProcessor.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statements;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\npublic final class EnumProcessor {\n  public static void clearEnum(ClassWrapper wrapper) {\n    StructClass cl = wrapper.getClassStruct();\n\n    // hide values/valueOf methods and super() invocations\n    for (MethodWrapper method : wrapper.getMethods()) {\n      StructMethod mt = method.methodStruct;\n      String name = mt.getName();\n      String descriptor = mt.getDescriptor();\n\n      if (\"values\".equals(name)) {\n        if (descriptor.equals(\"()[L\" + cl.qualifiedName + \";\")) {\n          wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(name, descriptor));\n        }\n      }\n      else if (\"valueOf\".equals(name)) {\n        if (descriptor.equals(\"(Ljava/lang/String;)L\" + cl.qualifiedName + \";\")) {\n          wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(name, descriptor));\n        }\n      }\n      else if (CodeConstants.INIT_NAME.equals(name)) {\n        Statement firstData = Statements.findFirstData(method.root);\n        if (firstData != null && !firstData.getExprents().isEmpty()) {\n          Exprent exprent = firstData.getExprents().get(0);\n          if (exprent.type == Exprent.EXPRENT_INVOCATION) {\n            InvocationExprent invExpr = (InvocationExprent)exprent;\n            if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false)) {\n              firstData.getExprents().remove(0);\n            }\n          }\n        }\n      }\n    }\n\n    // hide synthetic fields of enum and it's constants\n    for (StructField fd : cl.getFields()) {\n      String descriptor = fd.getDescriptor();\n      if (fd.isSynthetic() && descriptor.equals(\"[L\" + cl.qualifiedName + \";\")) {\n        wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), descriptor));\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/Fernflower.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.extern.*;\nimport org.jetbrains.java.decompiler.modules.renamer.ConverterHelper;\nimport org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter;\nimport org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;\nimport org.jetbrains.java.decompiler.struct.IDecompiledData;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructContext;\nimport org.jetbrains.java.decompiler.struct.lazy.LazyLoader;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.io.File;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\npublic class Fernflower implements IDecompiledData {\n  private final StructContext structContext;\n  private final ClassesProcessor classProcessor;\n  private final IIdentifierRenamer helper;\n  private final IdentifierConverter converter;\n\n  public Fernflower(IBytecodeProvider provider,\n                    IResultSaver saver,\n                    @Nullable Map<String, Object> customProperties,\n                    IFernflowerLogger logger,\n                    @Nullable CancellationManager cancellationManager) {\n    Map<String, Object> properties = new HashMap<>(IFernflowerPreferences.DEFAULTS);\n    if (customProperties != null) {\n      properties.putAll(customProperties);\n    }\n\n    String level = (String)properties.get(IFernflowerPreferences.LOG_LEVEL);\n    if (level != null) {\n      try {\n        logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.ENGLISH)));\n      }\n      catch (IllegalArgumentException ignore) { }\n    }\n\n    structContext = new StructContext(saver, this, new LazyLoader(provider));\n    classProcessor = new ClassesProcessor(structContext);\n\n    PoolInterceptor interceptor = null;\n    if (\"1\".equals(properties.get(IFernflowerPreferences.RENAME_ENTITIES))) {\n      helper = loadHelper((String)properties.get(IFernflowerPreferences.USER_RENAMER_CLASS), logger);\n      interceptor = new PoolInterceptor();\n      converter = new IdentifierConverter(structContext, helper, interceptor);\n    }\n    else {\n      helper = null;\n      converter = null;\n    }\n\n    DecompilerContext context = new DecompilerContext(properties, logger, structContext, classProcessor, interceptor, cancellationManager);\n    DecompilerContext.setCurrentContext(context);\n  }\n\n  public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> customProperties, IFernflowerLogger logger) {\n    this(provider, saver, customProperties, logger, null);\n  }\n\n  private static IIdentifierRenamer loadHelper(String className, IFernflowerLogger logger) {\n    if (className != null) {\n      try {\n        Class<?> renamerClass = Fernflower.class.getClassLoader().loadClass(className);\n        return (IIdentifierRenamer) renamerClass.getDeclaredConstructor().newInstance();\n      }\n      catch (Exception e) {\n        logger.writeMessage(\"Cannot load renamer '\" + className + \"'\", IFernflowerLogger.Severity.WARN, e);\n      }\n    }\n\n    return new ConverterHelper();\n  }\n\n  public void addSource(File source) {\n    structContext.addSpace(source, true);\n  }\n\n  public void addLibrary(File library) {\n    structContext.addSpace(library, false);\n  }\n\n  public void decompileContext() {\n    if (converter != null) {\n      converter.rename();\n    }\n\n    classProcessor.loadClasses(helper);\n\n    structContext.saveContext();\n  }\n\n  public void clearContext() {\n    DecompilerContext.setCurrentContext(null);\n  }\n\n  @Override\n  public String getClassEntryName(StructClass cl, String entryName) {\n    ClassNode node = classProcessor.getMapRootClasses().get(cl.qualifiedName);\n    if (node.type != ClassNode.CLASS_ROOT) {\n      return null;\n    }\n    else if (converter != null) {\n      String simpleClassName = cl.qualifiedName.substring(cl.qualifiedName.lastIndexOf('/') + 1);\n      return entryName.substring(0, entryName.lastIndexOf('/') + 1) + simpleClassName + \".java\";\n    }\n    else {\n      return entryName.substring(0, entryName.lastIndexOf(\".class\")) + \".java\";\n    }\n  }\n\n  @Override\n  public String getClassContent(StructClass cl) {\n    try {\n      TextBuffer buffer = new TextBuffer(ClassesProcessor.AVERAGE_CLASS_SIZE);\n      buffer.append(DecompilerContext.getProperty(IFernflowerPreferences.BANNER).toString());\n      classProcessor.writeClass(cl, buffer);\n      return buffer.toString();\n    }\n    catch (CancellationManager.CanceledException e) {\n      throw e;\n    }\n    catch (Throwable t) {\n      DecompilerContext.getLogger().writeMessage(\"Class \" + cl.qualifiedName + \" couldn't be fully decompiled.\", t);\n      return null;\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/InitializerProcessor.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statements;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic final class InitializerProcessor {\n  public static void extractInitializers(ClassWrapper wrapper) {\n    MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.CLINIT_NAME, \"()V\");\n    if (method != null && method.root != null) {  // successfully decompiled static constructor\n      extractStaticInitializers(wrapper, method);\n    }\n\n    extractDynamicInitializers(wrapper);\n\n    // required e.g. if anonymous class is being decompiled as a standard one.\n    // This can happen if InnerClasses attributes are erased\n    liftConstructor(wrapper);\n\n    if (DecompilerContext.getOption(IFernflowerPreferences.HIDE_EMPTY_SUPER)) {\n      hideEmptySuper(wrapper);\n    }\n  }\n\n  private static void liftConstructor(ClassWrapper wrapper) {\n    for (MethodWrapper method : wrapper.getMethods()) {\n      if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) {\n        Statement firstData = Statements.findFirstData(method.root);\n        if (firstData == null) {\n          return;\n        }\n\n        int index = 0;\n        List<Exprent> lstExprents = firstData.getExprents();\n\n        for (Exprent exprent : lstExprents) {\n          int action = 0;\n\n          if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n            AssignmentExprent assignExpr = (AssignmentExprent)exprent;\n            if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD && assignExpr.getRight().type == Exprent.EXPRENT_VAR) {\n              FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();\n              if (fExpr.getClassname().equals(wrapper.getClassStruct().qualifiedName)) {\n                StructField structField = wrapper.getClassStruct().getField(fExpr.getName(), fExpr.getDescriptor().descriptorString);\n                if (structField != null && structField.hasModifier(CodeConstants.ACC_FINAL)) {\n                  action = 1;\n                }\n              }\n            }\n          }\n          else if (index > 0 && exprent.type == Exprent.EXPRENT_INVOCATION &&\n                   Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, true)) {\n            // this() or super()\n            lstExprents.add(0, lstExprents.remove(index));\n            action = 2;\n          }\n\n          if (action != 1) {\n            break;\n          }\n\n          index++;\n        }\n      }\n    }\n  }\n\n  private static void hideEmptySuper(ClassWrapper wrapper) {\n    for (MethodWrapper method : wrapper.getMethods()) {\n      if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) {\n        Statement firstData = Statements.findFirstData(method.root);\n        if (firstData == null || firstData.getExprents().isEmpty()) {\n          return;\n        }\n\n        Exprent exprent = firstData.getExprents().get(0);\n        if (exprent.type == Exprent.EXPRENT_INVOCATION) {\n          InvocationExprent invExpr = (InvocationExprent)exprent;\n          if (Statements.isInvocationInitConstructor(invExpr, method, wrapper, false) && invExpr.getParameters().isEmpty()) {\n            firstData.getExprents().remove(0);\n          }\n        }\n      }\n    }\n  }\n\n  private static void extractStaticInitializers(ClassWrapper wrapper, MethodWrapper method) {\n    RootStatement root = method.root;\n    StructClass cl = wrapper.getClassStruct();\n    Statement firstData = Statements.findFirstData(root);\n    if (firstData != null) {\n      boolean inlineInitializers = cl.hasModifier(CodeConstants.ACC_INTERFACE) || cl.hasModifier(CodeConstants.ACC_ENUM);\n\n      while (!firstData.getExprents().isEmpty()) {\n        Exprent exprent = firstData.getExprents().get(0);\n\n        boolean found = false;\n\n        if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n          AssignmentExprent assignExpr = (AssignmentExprent)exprent;\n          if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {\n            FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();\n            if (fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) &&\n                cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) {\n\n              // interfaces fields should always be initialized inline\n              if (inlineInitializers || isExprentIndependent(assignExpr.getRight(), method)) {\n                String keyField = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString);\n                if (!wrapper.getStaticFieldInitializers().containsKey(keyField)) {\n                  wrapper.getStaticFieldInitializers().addWithKey(assignExpr.getRight(), keyField);\n                  firstData.getExprents().remove(0);\n                  found = true;\n                }\n              }\n            }\n          }\n        }\n\n        if (!found) {\n          break;\n        }\n      }\n    }\n  }\n\n  private static void extractDynamicInitializers(ClassWrapper wrapper) {\n    StructClass cl = wrapper.getClassStruct();\n\n    boolean isAnonymous = DecompilerContext.getClassProcessor().getMapRootClasses().get(cl.qualifiedName).type == ClassNode.CLASS_ANONYMOUS;\n\n    List<List<Exprent>> lstFirst = new ArrayList<>();\n    List<MethodWrapper> lstMethodWrappers = new ArrayList<>();\n\n    for (MethodWrapper method : wrapper.getMethods()) {\n      if (CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) && method.root != null) { // successfully decompiled constructor\n        Statement firstData = Statements.findFirstData(method.root);\n        if (firstData == null || firstData.getExprents().isEmpty()) {\n          return;\n        }\n        lstFirst.add(firstData.getExprents());\n        lstMethodWrappers.add(method);\n\n        Exprent exprent = firstData.getExprents().get(0);\n        if (!isAnonymous) { // FIXME: doesn't make sense\n          if (exprent.type != Exprent.EXPRENT_INVOCATION ||\n              !Statements.isInvocationInitConstructor((InvocationExprent)exprent, method, wrapper, false)) {\n            return;\n          }\n        }\n      }\n    }\n\n    if (lstFirst.isEmpty()) {\n      return;\n    }\n\n    while (true) {\n      String fieldWithDescr = null;\n      Exprent value = null;\n\n      for (int i = 0; i < lstFirst.size(); i++) {\n        List<Exprent> lst = lstFirst.get(i);\n\n        if (lst.size() < (isAnonymous ? 1 : 2)) {\n          return;\n        }\n\n        Exprent exprent = lst.get(isAnonymous ? 0 : 1);\n\n        boolean found = false;\n\n        if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n          AssignmentExprent assignExpr = (AssignmentExprent)exprent;\n          if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {\n            FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();\n            if (!fExpr.isStatic() && fExpr.getClassname().equals(cl.qualifiedName) &&\n                cl.hasField(fExpr.getName(), fExpr.getDescriptor().descriptorString)) { // check for the physical existence of the field. Could be defined in a superclass.\n\n              if (isExprentIndependent(assignExpr.getRight(), lstMethodWrappers.get(i))) {\n                String fieldKey = InterpreterUtil.makeUniqueKey(fExpr.getName(), fExpr.getDescriptor().descriptorString);\n                if (fieldWithDescr == null) {\n                  fieldWithDescr = fieldKey;\n                  value = assignExpr.getRight();\n                }\n                else {\n                  if (!fieldWithDescr.equals(fieldKey) ||\n                      !value.equals(assignExpr.getRight())) {\n                    return;\n                  }\n                }\n                found = true;\n              }\n            }\n          }\n        }\n\n        if (!found) {\n          return;\n        }\n      }\n\n      if (!wrapper.getDynamicFieldInitializers().containsKey(fieldWithDescr)) {\n        wrapper.getDynamicFieldInitializers().addWithKey(value, fieldWithDescr);\n\n        for (List<Exprent> lst : lstFirst) {\n          lst.remove(isAnonymous ? 0 : 1);\n        }\n      }\n      else {\n        return;\n      }\n    }\n  }\n\n  private static boolean isExprentIndependent(Exprent exprent, MethodWrapper method) {\n    List<Exprent> lst = exprent.getAllExprents(true);\n    lst.add(exprent);\n\n    for (Exprent expr : lst) {\n      switch (expr.type) {\n        case Exprent.EXPRENT_VAR -> {\n          VarVersionPair varPair = new VarVersionPair((VarExprent)expr);\n          if (!method.varproc.getExternalVars().contains(varPair)) {\n            String varName = method.varproc.getVarName(varPair);\n            if (!varName.equals(\"this\") && !varName.endsWith(\".this\")) { // FIXME: remove direct comparison with strings\n              return false;\n            }\n          }\n        }\n        case Exprent.EXPRENT_FIELD -> {\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/collectors/BytecodeMappingTracer.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.collectors;\n\nimport org.jetbrains.java.decompiler.struct.attr.StructLineNumberTableAttribute;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class BytecodeMappingTracer {\n  public static final BytecodeMappingTracer DUMMY = new BytecodeMappingTracer();\n\n  private int currentSourceLine;\n  private StructLineNumberTableAttribute lineNumberTable = null;\n  private final Map<Integer, Integer> mapping = new HashMap<>();  // bytecode offset, source line\n\n  public BytecodeMappingTracer() { }\n\n  public BytecodeMappingTracer(int initial_source_line) {\n    currentSourceLine = initial_source_line;\n  }\n\n  public void incrementCurrentSourceLine() {\n    currentSourceLine++;\n  }\n\n  public void incrementCurrentSourceLine(int number_lines) {\n    currentSourceLine += number_lines;\n  }\n\n  public void addMapping(int bytecode_offset) {\n    mapping.putIfAbsent(bytecode_offset, currentSourceLine);\n  }\n\n  public void addMapping(Set<Integer> bytecode_offsets) {\n    if (bytecode_offsets != null) {\n      for (Integer bytecode_offset : bytecode_offsets) {\n        addMapping(bytecode_offset);\n      }\n    }\n  }\n\n  public void addTracer(BytecodeMappingTracer tracer) {\n    if (tracer != null) {\n      for (Entry<Integer, Integer> entry : tracer.mapping.entrySet()) {\n        mapping.putIfAbsent(entry.getKey(), entry.getValue());\n      }\n    }\n  }\n\n  public Map<Integer, Integer> getMapping() {\n    return mapping;\n  }\n\n  public int getCurrentSourceLine() {\n    return currentSourceLine;\n  }\n\n  public void setCurrentSourceLine(int currentSourceLine) {\n    this.currentSourceLine = currentSourceLine;\n  }\n\n  public void setLineNumberTable(StructLineNumberTableAttribute lineNumberTable) {\n    this.lineNumberTable = lineNumberTable;\n  }\n\n  private final Set<Integer> unmappedLines = new HashSet<>();\n\n  public Set<Integer> getUnmappedLines() {\n    return unmappedLines;\n  }\n\n  public Map<Integer, Integer> getOriginalLinesMapping() {\n    if (lineNumberTable == null) {\n      return Collections.emptyMap();\n    }\n\n    Map<Integer, Integer> res = new HashMap<>();\n\n    // first match offsets from line number table\n    int[] data = lineNumberTable.getRawData();\n    for (int i = 0; i < data.length; i += 2) {\n      int originalOffset = data[i];\n      int originalLine = data[i + 1];\n      Integer newLine = mapping.get(originalOffset);\n      if (newLine != null) {\n        res.put(originalLine, newLine);\n      }\n      else {\n        unmappedLines.add(originalLine);\n      }\n    }\n\n    // now match offsets from decompiler mapping\n    for (Entry<Integer, Integer> entry : mapping.entrySet()) {\n      int originalLine = lineNumberTable.findLineNumber(entry.getKey());\n      if (originalLine > -1 && !res.containsKey(originalLine)) {\n        res.put(originalLine, entry.getValue());\n        unmappedLines.remove(originalLine);\n      }\n    }\n    return res;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/collectors/BytecodeSourceMapper.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.collectors;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class BytecodeSourceMapper {\n  private int offset_total;\n\n  // class, method, bytecode offset, source line\n  private final Map<String, Map<String, Map<Integer, Integer>>> mapping = new LinkedHashMap<>();\n\n  // original line to decompiled line\n  private final Map<Integer, Integer> linesMapping = new HashMap<>();\n  private final Set<Integer> unmappedLines = new TreeSet<>();\n\n  public void addMapping(String className, String methodName, int bytecodeOffset, int sourceLine) {\n    Map<String, Map<Integer, Integer>> class_mapping = mapping.computeIfAbsent(className, k -> new LinkedHashMap<>()); // need to preserve order\n    Map<Integer, Integer> method_mapping = class_mapping.computeIfAbsent(methodName, k -> new HashMap<>());\n\n    // don't overwrite\n    method_mapping.putIfAbsent(bytecodeOffset, sourceLine);\n  }\n\n  public void addTracer(String className, String methodName, BytecodeMappingTracer tracer) {\n    for (Entry<Integer, Integer> entry : tracer.getMapping().entrySet()) {\n      addMapping(className, methodName, entry.getKey(), entry.getValue());\n    }\n    linesMapping.putAll(tracer.getOriginalLinesMapping());\n    unmappedLines.addAll(tracer.getUnmappedLines());\n  }\n\n  public void dumpMapping(TextBuffer buffer, boolean offsetsToHex) {\n    if (mapping.isEmpty() && linesMapping.isEmpty()) {\n      return;\n    }\n\n    String lineSeparator = DecompilerContext.getNewLineSeparator();\n\n    for (Entry<String, Map<String, Map<Integer, Integer>>> class_entry : mapping.entrySet()) {\n      Map<String, Map<Integer, Integer>> class_mapping = class_entry.getValue();\n      buffer.append(\"class '\" + class_entry.getKey() + \"' {\" + lineSeparator);\n\n      boolean is_first_method = true;\n      for (Entry<String, Map<Integer, Integer>> method_entry : class_mapping.entrySet()) {\n        Map<Integer, Integer> method_mapping = method_entry.getValue();\n\n        if (!is_first_method) {\n          buffer.appendLineSeparator();\n        }\n\n        buffer.appendIndent(1).append(\"method '\" + method_entry.getKey() + \"' {\" + lineSeparator);\n\n        List<Integer> lstBytecodeOffsets = new ArrayList<>(method_mapping.keySet());\n        Collections.sort(lstBytecodeOffsets);\n\n        for (Integer offset : lstBytecodeOffsets) {\n          Integer line = method_mapping.get(offset);\n\n          String strOffset = offsetsToHex ? Integer.toHexString(offset) : line.toString();\n          buffer.appendIndent(2).append(strOffset).appendIndent(2).append((line + offset_total) + lineSeparator);\n        }\n        buffer.appendIndent(1).append(\"}\").appendLineSeparator();\n\n        is_first_method = false;\n      }\n\n      buffer.append(\"}\").appendLineSeparator().appendLineSeparator();\n    }\n\n    // lines mapping\n    buffer.append(\"Lines mapping:\").appendLineSeparator();\n    Map<Integer, Integer> sorted = new TreeMap<>(linesMapping);\n    for (Entry<Integer, Integer> entry : sorted.entrySet()) {\n      buffer.append(entry.getKey()).append(\" <-> \").append(entry.getValue() + offset_total + 1).appendLineSeparator();\n    }\n\n    if (!unmappedLines.isEmpty()) {\n      buffer.append(\"Not mapped:\").appendLineSeparator();\n      for (Integer line : unmappedLines) {\n        if (!linesMapping.containsKey(line)) {\n          buffer.append(line).appendLineSeparator();\n        }\n      }\n    }\n  }\n\n  public void addTotalOffset(int offset_total) {\n    this.offset_total += offset_total;\n  }\n\n  /**\n   * Original to decompiled line mapping.\n   */\n  public int[] getOriginalLinesMapping() {\n    int[] res = new int[linesMapping.size() * 2];\n    int i = 0;\n    for (Entry<Integer, Integer> entry : linesMapping.entrySet()) {\n      res[i] = entry.getKey();\n      unmappedLines.remove(entry.getKey());\n      res[i + 1] = entry.getValue() + offset_total + 1; // make it 1 based\n      i += 2;\n    }\n    return res;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.collectors;\n\npublic class CounterContainer {\n  public static final int STATEMENT_COUNTER = 0;\n  public static final int EXPRESSION_COUNTER = 1;\n  public static final int VAR_COUNTER = 2;\n\n  private final int[] values = new int[]{1, 1, 1};\n\n  public void setCounter(int counter, int value) {\n    values[counter] = value;\n  }\n\n  public int getCounter(int counter) {\n    return values[counter];\n  }\n\n  public int getCounterAndIncrement(int counter) {\n    return values[counter]++;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.main.collectors;\n\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructContext;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructInnerClassesAttribute;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class ImportCollector {\n  private static final String JAVA_LANG_PACKAGE = \"java.lang\";\n\n  private final Map<String, String> mapSimpleNames = new HashMap<>();\n  private final Set<String> setNotImportedNames = new HashSet<>();\n  // set of field names in this class and all its predecessors.\n  private final Set<String> setFieldNames = new HashSet<>();\n  private final Set<String> setInnerClassNames = new HashSet<>();\n  private final String currentPackageSlash;\n  private final String currentPackagePoint;\n\n  public ImportCollector(ClassNode root) {\n    String clName = root.classStruct.qualifiedName;\n    int index = clName.lastIndexOf('/');\n    if (index >= 0) {\n      String packageName = clName.substring(0, index);\n      currentPackageSlash = packageName + '/';\n      currentPackagePoint = packageName.replace('/', '.');\n    }\n    else {\n      currentPackageSlash = \"\";\n      currentPackagePoint = \"\";\n    }\n\n    Map<String, StructClass> classes = DecompilerContext.getStructContext().getClasses();\n    LinkedList<String> queue = new LinkedList<>();\n    Set<StructClass> processedClasses = new HashSet<>();\n    StructClass currentClass = root.classStruct;\n    while (currentClass != null) {\n      processedClasses.add(currentClass);\n      if (currentClass.superClass != null) {\n        queue.add(currentClass.superClass.getString());\n      }\n\n      Collections.addAll(queue, currentClass.getInterfaceNames());\n\n      // all field names for the current class ..\n      for (StructField f : currentClass.getFields()) {\n        setFieldNames.add(f.getName());\n      }\n\n      // .. all inner classes for the current class ..\n      StructInnerClassesAttribute attribute = currentClass.getAttribute(StructGeneralAttribute.ATTRIBUTE_INNER_CLASSES);\n      if (attribute != null) {\n        for (StructInnerClassesAttribute.Entry entry : attribute.getEntries()) {\n          if (entry.enclosingName != null && entry.enclosingName.equals(currentClass.qualifiedName)) {\n            setInnerClassNames.add(entry.simpleName);\n          }\n        }\n      }\n\n      // .. and traverse through parent.\n      do {\n        currentClass = queue.isEmpty() ? null : classes.get(queue.removeFirst());\n\n        if (currentClass != null && processedClasses.contains(currentClass)) {\n          // Class already processed, skipping.\n\n          // This may be sign of circularity in the class hierarchy but in most cases this mean that same interface\n          // are listed as implemented several times in the class hierarchy.\n          currentClass = null;\n        }\n      } while (currentClass == null && !queue.isEmpty());\n    }\n  }\n\n  /**\n   * Check whether the package-less name ClassName is shaded by variable in a context of\n   * the decompiled class\n   * @param classToName - pkg.name.ClassName - class to find the nested name for\n   * @return ClassName if the name is not shaded by local field, pkg.name.ClassName otherwise\n   */\n  public String getNestedNameInClassContext(String classToName) {\n    String nestedName = getNestedName(classToName);\n    if (setFieldNames.contains(nestedName)) {\n      return classToName;\n    }\n    else {\n      return nestedName;\n    }\n  }\n\n  public String getNestedName(String fullName) {\n    return getNestedName(fullName, true);\n  }\n\n  public String getNestedName(String fullName, boolean imported) {\n    ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(fullName.replace('.', '/')); //todo[r.sh] anonymous classes?\n\n    String nestedName;\n    if (node != null && node.classStruct.isOwn()) {\n      nestedName = node.simpleName;\n\n      while (node.parent != null && node.type == ClassNode.CLASS_MEMBER) {\n        //noinspection StringConcatenationInLoop\n        nestedName = node.parent.simpleName + '.' + nestedName;\n        node = node.parent;\n      }\n\n      if (node.type == ClassNode.CLASS_ROOT) {\n        fullName = node.classStruct.qualifiedName;\n        fullName = fullName.replace('/', '.');\n      }\n      else {\n        return nestedName;\n      }\n    }\n    else {\n      fullName = fullName.replace('/', '.');\n      int lastDot = fullName.lastIndexOf('.');\n      if (lastDot != -1) {\n        nestedName = fullName.substring(lastDot + 1).replace(\"$\", \".\");\n      } else {\n        nestedName = fullName;\n      }\n    }\n\n    String outerShortName = fullName;\n    String packageName = \"\";\n\n    int lastDot = fullName.lastIndexOf('.');\n    if (lastDot >= 0) {\n      int firstNestedDot = nestedName.indexOf(\".\");\n      if (firstNestedDot >= 0) {\n        outerShortName = nestedName.substring(0, firstNestedDot);\n      } else {\n        outerShortName = nestedName;\n      }\n      packageName = fullName.substring(0, lastDot);\n    }\n\n    StructContext context = DecompilerContext.getStructContext();\n\n    // check for another class which could 'shadow' this one. Three cases:\n    // 1) class with the same short name in the current package\n    // 2) class with the same short name in the default package\n    // 3) inner class with the same short name in the current class, a super class, or an implemented interface\n    boolean existsDefaultClass =\n      (context.getClass(currentPackageSlash + outerShortName) != null && !packageName.equals(currentPackagePoint)) || // current package\n      (context.getClass(outerShortName) != null && !currentPackagePoint.isEmpty()) || // default package\n      setInnerClassNames.contains(outerShortName); // inner class\n\n    if (existsDefaultClass || (mapSimpleNames.containsKey(outerShortName) && !packageName.equals(mapSimpleNames.get(outerShortName)))) {\n      return packageName + \".\" + nestedName;\n    }\n    else if (!mapSimpleNames.containsKey(outerShortName)) {\n      mapSimpleNames.put(outerShortName, packageName);\n      if (!imported) {\n        setNotImportedNames.add(outerShortName);\n      }\n    }\n\n    return nestedName;\n  }\n\n  public void writeImports(TextBuffer buffer, boolean addSeparator) {\n    List<String> imports = packImports();\n    for (String line : imports) {\n      buffer.append(\"import \").append(line).append(';').appendLineSeparator();\n    }\n    if (addSeparator && !imports.isEmpty()) {\n      buffer.appendLineSeparator();\n    }\n  }\n\n  private List<String> packImports() {\n    return mapSimpleNames.entrySet().stream()\n      .filter(ent ->\n                // exclude the current class or one of the nested ones\n                // empty, java.lang and the current packages\n                !setNotImportedNames.contains(ent.getKey()) &&\n                !ent.getValue().isEmpty() &&\n                !JAVA_LANG_PACKAGE.equals(ent.getValue()) &&\n                !ent.getValue().equals(currentPackagePoint)\n      )\n      .sorted(Map.Entry.<String, String>comparingByValue().thenComparing(Map.Entry.comparingByKey()))\n      .map(ent -> ent.getValue() + \".\" + ent.getKey())\n      .collect(Collectors.toList());\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.collectors;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class VarNamesCollector {\n\n  private final Set<String> usedNames = new HashSet<>();\n\n  public VarNamesCollector() { }\n\n  public VarNamesCollector(Collection<String> setNames) {\n    usedNames.addAll(setNames);\n  }\n\n  public void addName(String value) {\n    usedNames.add(value);\n  }\n\n  public String getFreeName(int index) {\n    return getFreeName(\"var\" + index);\n  }\n\n  public String getFreeName(String proposition) {\n    while (usedNames.contains(proposition)) {\n      proposition += \"x\";\n    }\n    usedNames.add(proposition);\n    return proposition;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.decompiler;\n\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.Fernflower;\nimport org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IResultSaver;\n\nimport java.io.File;\nimport java.util.Map;\n\n@SuppressWarnings(\"unused\")\npublic class BaseDecompiler {\n  private final Fernflower engine;\n\n  public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, @Nullable Map<String, Object> options, IFernflowerLogger logger) {\n    this(provider, saver, options, logger, null);\n  }\n\n  public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, @Nullable Map<String, Object> options, IFernflowerLogger logger,\n                        @Nullable CancellationManager cancellationManager) {\n    engine = new Fernflower(provider, saver, options, logger, cancellationManager);\n  }\n\n  public void addSource(File source) {\n    engine.addSource(source);\n  }\n\n  public void addLibrary(File library) {\n    engine.addLibrary(library);\n  }\n\n  public void decompileContext() {\n    try {\n      engine.decompileContext();\n    }\n    finally {\n      engine.clearContext();\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.decompiler;\n\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.Fernflower;\nimport org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IResultSaver;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.io.*;\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\nimport java.util.jar.JarOutputStream;\nimport java.util.jar.Manifest;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport java.util.zip.ZipOutputStream;\n\npublic class ConsoleDecompiler implements IBytecodeProvider, IResultSaver {\n  @SuppressWarnings(\"UseOfSystemOutOrSystemErr\")\n  public static void main(String[] args) {\n    if (args.length < 2) {\n      System.out.println(\n        \"Usage: java -jar fernflower.jar [-<option>=<value>]* [<source>]+ <destination>\\n\" +\n        \"Example: java -jar fernflower.jar -dgs=true c:\\\\my\\\\source\\\\ c:\\\\my.jar d:\\\\decompiled\\\\\");\n      return;\n    }\n\n    Map<String, Object> mapOptions = new HashMap<>();\n    List<File> sources = new ArrayList<>();\n    List<File> libraries = new ArrayList<>();\n\n    boolean isOption = true;\n    for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination\n      String arg = args[i];\n\n      if (isOption && arg.length() > 5 && arg.charAt(0) == '-' && arg.charAt(4) == '=') {\n        String value = arg.substring(5);\n        if (\"true\".equalsIgnoreCase(value)) {\n          value = \"1\";\n        }\n        else if (\"false\".equalsIgnoreCase(value)) {\n          value = \"0\";\n        }\n\n        mapOptions.put(arg.substring(1, 4), value);\n      }\n      else {\n        isOption = false;\n\n        if (arg.startsWith(\"-e=\")) {\n          addPath(libraries, arg.substring(3));\n        }\n        else {\n          addPath(sources, arg);\n        }\n      }\n    }\n\n    if (sources.isEmpty()) {\n      System.out.println(\"error: no sources given\");\n      return;\n    }\n\n    File destination = new File(args[args.length - 1]);\n    if (!destination.isDirectory()) {\n      System.out.println(\"error: destination '\" + destination + \"' is not a directory\");\n      return;\n    }\n\n    PrintStreamLogger logger = new PrintStreamLogger(System.out);\n    ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger);\n\n    for (File library : libraries) {\n      decompiler.addLibrary(library);\n    }\n    for (File source : sources) {\n      decompiler.addSource(source);\n    }\n\n    decompiler.decompileContext();\n  }\n\n  @SuppressWarnings(\"UseOfSystemOutOrSystemErr\")\n  private static void addPath(List<? super File> list, String path) {\n    File file = new File(path);\n    if (file.exists()) {\n      list.add(file);\n    }\n    else {\n      System.out.println(\"warn: missing '\" + path + \"', ignored\");\n    }\n  }\n\n  // *******************************************************************\n  // Implementation\n  // *******************************************************************\n\n  private final File root;\n  private final Fernflower engine;\n  private final Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<>();\n  private final Map<String, Set<String>> mapArchiveEntries = new HashMap<>();\n\n  public ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) {\n    root = destination;\n    engine = new Fernflower(this, this, options, logger);\n  }\n\n  public ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger, CancellationManager cancellationManager) {\n    root = destination;\n    engine = new Fernflower(this, this, options, logger, cancellationManager);\n  }\n\n  public void addSource(File source) {\n    engine.addSource(source);\n  }\n\n  public void addLibrary(File library) {\n    engine.addLibrary(library);\n  }\n\n  public void decompileContext() {\n    try {\n      engine.decompileContext();\n    }\n    finally {\n      engine.clearContext();\n    }\n  }\n\n  // *******************************************************************\n  // Interface IBytecodeProvider\n  // *******************************************************************\n\n  @Override\n  public byte[] getBytecode(String externalPath, String internalPath) throws IOException {\n    File file = new File(externalPath);\n    if (internalPath == null) {\n      return InterpreterUtil.getBytes(file);\n    }\n    else {\n      try (ZipFile archive = new ZipFile(file)) {\n        ZipEntry entry = archive.getEntry(internalPath);\n        if (entry == null) throw new IOException(\"Entry not found: \" + internalPath);\n        return InterpreterUtil.getBytes(archive, entry);\n      }\n    }\n  }\n\n  // *******************************************************************\n  // Interface IResultSaver\n  // *******************************************************************\n\n  private String getAbsolutePath(String path) {\n    return new File(root, path).getAbsolutePath();\n  }\n\n  @Override\n  public void saveFolder(String path) {\n    File dir = new File(getAbsolutePath(path));\n    if (!(dir.mkdirs() || dir.isDirectory())) {\n      throw new RuntimeException(\"Cannot create directory \" + dir);\n    }\n  }\n\n  @Override\n  public void copyFile(String source, String path, String entryName) {\n    try {\n      InterpreterUtil.copyFile(new File(source), new File(getAbsolutePath(path), entryName));\n    }\n    catch (IOException ex) {\n      DecompilerContext.getLogger().writeMessage(\"Cannot copy \" + source + \" to \" + entryName, ex);\n    }\n  }\n\n  @Override\n  public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {\n    File file = new File(getAbsolutePath(path), entryName);\n    try (Writer out = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {\n      out.write(content);\n    }\n    catch (IOException ex) {\n      DecompilerContext.getLogger().writeMessage(\"Cannot write class file \" + file, ex);\n    }\n  }\n\n  @Override\n  public void createArchive(String path, String archiveName, Manifest manifest) {\n    File file = new File(getAbsolutePath(path), archiveName);\n    try {\n      if (!(file.createNewFile() || file.isFile())) {\n        throw new IOException(\"Cannot create file \" + file);\n      }\n\n      FileOutputStream fileStream = new FileOutputStream(file);\n      ZipOutputStream zipStream = manifest != null ? new JarOutputStream(fileStream, manifest) : new ZipOutputStream(fileStream);\n      mapArchiveStreams.put(file.getPath(), zipStream);\n    }\n    catch (IOException ex) {\n      DecompilerContext.getLogger().writeMessage(\"Cannot create archive \" + file, ex);\n    }\n  }\n\n  @Override\n  public void saveDirEntry(String path, String archiveName, String entryName) {\n    saveClassEntry(path, archiveName, null, entryName, null);\n  }\n\n  @Override\n  public void copyEntry(String source, String path, String archiveName, String entryName) {\n    String file = new File(getAbsolutePath(path), archiveName).getPath();\n\n    if (!checkEntry(entryName, file)) {\n      return;\n    }\n\n    try (ZipFile srcArchive = new ZipFile(new File(source))) {\n      ZipEntry entry = srcArchive.getEntry(entryName);\n      if (entry != null) {\n        try (InputStream in = srcArchive.getInputStream(entry)) {\n          ZipOutputStream out = mapArchiveStreams.get(file);\n          out.putNextEntry(new ZipEntry(entryName));\n          InterpreterUtil.copyStream(in, out);\n        }\n      }\n    }\n    catch (IOException ex) {\n      String message = \"Cannot copy entry \" + entryName + \" from \" + source + \" to \" + file;\n      DecompilerContext.getLogger().writeMessage(message, ex);\n    }\n  }\n\n  @Override\n  public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) {\n    String file = new File(getAbsolutePath(path), archiveName).getPath();\n\n    if (!checkEntry(entryName, file)) {\n      return;\n    }\n\n    try {\n      ZipOutputStream out = mapArchiveStreams.get(file);\n      out.putNextEntry(new ZipEntry(entryName));\n      if (content != null) {\n        out.write(content.getBytes(StandardCharsets.UTF_8));\n      }\n    }\n    catch (IOException ex) {\n      String message = \"Cannot write entry \" + entryName + \" to \" + file;\n      DecompilerContext.getLogger().writeMessage(message, ex);\n    }\n  }\n\n  private boolean checkEntry(String entryName, String file) {\n    Set<String> set = mapArchiveEntries.computeIfAbsent(file, k -> new HashSet<>());\n\n    boolean added = set.add(entryName);\n    if (!added) {\n      String message = \"Zip entry \" + entryName + \" already exists in \" + file;\n      DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n    }\n    return added;\n  }\n\n  @Override\n  public void closeArchive(String path, String archiveName) {\n    String file = new File(getAbsolutePath(path), archiveName).getPath();\n    try {\n      mapArchiveEntries.remove(file);\n      mapArchiveStreams.remove(file).close();\n    }\n    catch (IOException ex) {\n      DecompilerContext.getLogger().writeMessage(\"Cannot close \" + file, IFernflowerLogger.Severity.WARN);\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.decompiler;\n\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.util.TextUtil;\n\nimport java.io.PrintStream;\n\npublic class PrintStreamLogger extends IFernflowerLogger {\n\n  private final PrintStream stream;\n  private int indent;\n\n  public PrintStreamLogger(PrintStream printStream) {\n    stream = printStream;\n    indent = 0;\n  }\n\n  @Override\n  public void writeMessage(String message, Severity severity) {\n    if (accepts(severity)) {\n      stream.println(severity.prefix + TextUtil.getIndentString(indent) + message);\n    }\n  }\n\n  @Override\n  public void writeMessage(String message, Severity severity, Throwable t) {\n    if (accepts(severity)) {\n      writeMessage(message, severity);\n      t.printStackTrace(stream);\n    }\n  }\n\n  @Override\n  public void startReadingClass(String className) {\n    if (accepts(Severity.INFO)) {\n      writeMessage(\"Decompiling class \" + className, Severity.INFO);\n      ++indent;\n    }\n  }\n\n  @Override\n  public void endReadingClass() {\n    if (accepts(Severity.INFO)) {\n      --indent;\n      writeMessage(\"... done\", Severity.INFO);\n    }\n  }\n\n  @Override\n  public void startClass(String className) {\n    if (accepts(Severity.INFO)) {\n      writeMessage(\"Processing class \" + className, Severity.TRACE);\n      ++indent;\n    }\n  }\n\n  @Override\n  public void endClass() {\n    if (accepts(Severity.INFO)) {\n      --indent;\n      writeMessage(\"... proceeded\", Severity.TRACE);\n    }\n  }\n\n  @Override\n  public void startMethod(String methodName) {\n    if (accepts(Severity.INFO)) {\n      writeMessage(\"Processing method \" + methodName, Severity.TRACE);\n      ++indent;\n    }\n  }\n\n  @Override\n  public void endMethod() {\n    if (accepts(Severity.INFO)) {\n      --indent;\n      writeMessage(\"... proceeded\", Severity.TRACE);\n    }\n  }\n\n  @Override\n  public void startWriteClass(String className) {\n    if (accepts(Severity.INFO)) {\n      writeMessage(\"Writing class \" + className, Severity.TRACE);\n      ++indent;\n    }\n  }\n\n  @Override\n  public void endWriteClass() {\n    if (accepts(Severity.INFO)) {\n      --indent;\n      writeMessage(\"... written\", Severity.TRACE);\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/extern/ClassFormatException.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.extern;\n\npublic class ClassFormatException extends RuntimeException {\n  public ClassFormatException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.extern;\n\nimport java.io.IOException;\n\npublic interface IBytecodeProvider {\n  byte[] getBytecode(String externalPath, String internalPath) throws IOException;\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.extern;\n\npublic abstract class IFernflowerLogger {\n\n  public enum Severity {\n    TRACE(\"TRACE: \"), INFO(\"INFO:  \"), WARN(\"WARN:  \"), ERROR(\"ERROR: \");\n\n    public final String prefix;\n\n    Severity(String prefix) {\n      this.prefix = prefix;\n    }\n  }\n\n  private Severity severity = Severity.INFO;\n\n  public boolean accepts(Severity severity) {\n    return severity.ordinal() >= this.severity.ordinal();\n  }\n\n  public void setSeverity(Severity severity) {\n    this.severity = severity;\n  }\n\n  public abstract void writeMessage(String message, Severity severity);\n\n  public abstract void writeMessage(String message, Severity severity, Throwable t);\n\n  public void writeMessage(String message, Throwable t) {\n    writeMessage(message, Severity.ERROR, t);\n  }\n\n  public void startReadingClass(String className) { }\n\n  public void endReadingClass() { }\n\n  public void startClass(String className) { }\n\n  public void endClass() { }\n\n  public void startMethod(String methodName) { }\n\n  public void endMethod() { }\n\n  public void startWriteClass(String className) { }\n\n  public void endWriteClass() { }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.extern;\n\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic interface IFernflowerPreferences {\n  String REMOVE_BRIDGE = \"rbr\";\n  String REMOVE_SYNTHETIC = \"rsy\";\n  String DECOMPILE_INNER = \"din\";\n  String DECOMPILE_CLASS_1_4 = \"dc4\";\n  String DECOMPILE_ASSERTIONS = \"das\";\n  String HIDE_EMPTY_SUPER = \"hes\";\n  String HIDE_DEFAULT_CONSTRUCTOR = \"hdc\";\n  String DECOMPILE_GENERIC_SIGNATURES = \"dgs\";\n  String NO_EXCEPTIONS_RETURN = \"ner\";\n  String ENSURE_SYNCHRONIZED_MONITOR = \"esm\";\n  String DECOMPILE_ENUM = \"den\";\n  String REMOVE_GET_CLASS_NEW = \"rgn\";\n  String LITERALS_AS_IS = \"lit\";\n  String BOOLEAN_TRUE_ONE = \"bto\";\n  String ASCII_STRING_CHARACTERS = \"asc\";\n  String SYNTHETIC_NOT_SET = \"nns\";\n  String UNDEFINED_PARAM_TYPE_OBJECT = \"uto\";\n  String USE_DEBUG_VAR_NAMES = \"udv\";\n  String USE_METHOD_PARAMETERS = \"ump\";\n  String REMOVE_EMPTY_RANGES = \"rer\";\n  String FINALLY_DEINLINE = \"fdi\";\n  String IDEA_NOT_NULL_ANNOTATION = \"inn\";\n  String LAMBDA_TO_ANONYMOUS_CLASS = \"lac\";\n  String BYTECODE_SOURCE_MAPPING = \"bsm\";\n  String IGNORE_INVALID_BYTECODE = \"iib\";\n  String VERIFY_ANONYMOUS_CLASSES = \"vac\";\n  String CONVERT_RECORD_PATTERN = \"crp\";\n  String CONVERT_PATTERN_SWITCH = \"cps\";\n\n  String LOG_LEVEL = \"log\";\n  String MAX_PROCESSING_METHOD = \"mpm\";\n  String RENAME_ENTITIES = \"ren\";\n  String USER_RENAMER_CLASS = \"urc\";\n  String NEW_LINE_SEPARATOR = \"nls\";\n  String INDENT_STRING = \"ind\";\n  String BANNER = \"ban\";\n\n  String DUMP_ORIGINAL_LINES = \"__dump_original_lines__\";\n  String UNIT_TEST_MODE = \"__unit_test_mode__\";\n\n  String LINE_SEPARATOR_WIN = \"\\r\\n\";\n  String LINE_SEPARATOR_UNX = \"\\n\";\n\n  Map<String, Object> DEFAULTS = getDefaults();\n\n  static Map<String, Object> getDefaults() {\n    Map<String, Object> defaults = new HashMap<>();\n\n    defaults.put(REMOVE_BRIDGE, \"1\");\n    defaults.put(REMOVE_SYNTHETIC, \"0\");\n    defaults.put(DECOMPILE_INNER, \"1\");\n    defaults.put(DECOMPILE_CLASS_1_4, \"1\");\n    defaults.put(DECOMPILE_ASSERTIONS, \"1\");\n    defaults.put(HIDE_EMPTY_SUPER, \"1\");\n    defaults.put(HIDE_DEFAULT_CONSTRUCTOR, \"1\");\n    defaults.put(DECOMPILE_GENERIC_SIGNATURES, \"0\");\n    defaults.put(NO_EXCEPTIONS_RETURN, \"1\");\n    defaults.put(ENSURE_SYNCHRONIZED_MONITOR, \"1\");\n    defaults.put(DECOMPILE_ENUM, \"1\");\n    defaults.put(REMOVE_GET_CLASS_NEW, \"1\");\n    defaults.put(LITERALS_AS_IS, \"0\");\n    defaults.put(BOOLEAN_TRUE_ONE, \"1\");\n    defaults.put(ASCII_STRING_CHARACTERS, \"0\");\n    defaults.put(SYNTHETIC_NOT_SET, \"0\");\n    defaults.put(UNDEFINED_PARAM_TYPE_OBJECT, \"1\");\n    defaults.put(USE_DEBUG_VAR_NAMES, \"1\");\n    defaults.put(USE_METHOD_PARAMETERS, \"1\");\n    defaults.put(REMOVE_EMPTY_RANGES, \"1\");\n    defaults.put(FINALLY_DEINLINE, \"1\");\n    defaults.put(IDEA_NOT_NULL_ANNOTATION, \"1\");\n    defaults.put(LAMBDA_TO_ANONYMOUS_CLASS, \"0\");\n    defaults.put(BYTECODE_SOURCE_MAPPING, \"0\");\n    defaults.put(IGNORE_INVALID_BYTECODE, \"0\");\n    defaults.put(VERIFY_ANONYMOUS_CLASSES, \"0\");\n    defaults.put(CONVERT_RECORD_PATTERN, \"0\");\n    defaults.put(CONVERT_PATTERN_SWITCH, \"0\");\n\n    defaults.put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name());\n    defaults.put(MAX_PROCESSING_METHOD, \"0\");\n    defaults.put(RENAME_ENTITIES, \"0\");\n    defaults.put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? \"0\" : \"1\"));\n    defaults.put(INDENT_STRING, \"   \");\n    defaults.put(BANNER, \"\");\n    defaults.put(UNIT_TEST_MODE, \"0\");\n    defaults.put(DUMP_ORIGINAL_LINES, \"0\");\n\n    return Collections.unmodifiableMap(defaults);\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.extern;\n\npublic interface IIdentifierRenamer {\n\n  enum Type {ELEMENT_CLASS, ELEMENT_FIELD, ELEMENT_METHOD}\n\n  boolean toBeRenamed(Type elementType, String className, String element, String descriptor);\n\n  String getNextClassName(String fullName, String shortName);\n\n  String getNextFieldName(String className, String field, String descriptor);\n\n  String getNextMethodName(String className, String method, String descriptor);\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.extern;\n\nimport java.util.jar.Manifest;\n\npublic interface IResultSaver {\n  void saveFolder(String path);\n\n  void copyFile(String source, String path, String entryName);\n\n  void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping);\n\n  void createArchive(String path, String archiveName, Manifest manifest);\n\n  void saveDirEntry(String path, String archiveName, String entryName);\n\n  void copyEntry(String source, String path, String archiveName, String entry);\n\n  void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content);\n\n  void closeArchive(String path, String archiveName);\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.main.rels;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructMethodParametersAttribute;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class ClassWrapper {\n  private final StructClass classStruct;\n  private final Set<String> hiddenMembers = new HashSet<>();\n  private final VBStyleCollection<Exprent, String> staticFieldInitializers = new VBStyleCollection<>();\n  private final VBStyleCollection<Exprent, String> dynamicFieldInitializers = new VBStyleCollection<>();\n  private final VBStyleCollection<MethodWrapper, String> methods = new VBStyleCollection<>();\n\n  public ClassWrapper(StructClass classStruct) {\n    this.classStruct = classStruct;\n  }\n\n  public void init() {\n    DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS, classStruct);\n    DecompilerContext.setProperty(DecompilerContext.CURRENT_CLASS_WRAPPER, this);\n    DecompilerContext.getLogger().startClass(classStruct.qualifiedName);\n\n    boolean testMode = DecompilerContext.getOption(IFernflowerPreferences.UNIT_TEST_MODE);\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n    for (StructMethod mt : classStruct.getMethods()) {\n      DecompilerContext.getLogger().startMethod(mt.getName() + \" \" + mt.getDescriptor());\n\n      MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n      VarProcessor varProc = new VarProcessor(classStruct, mt, md);\n      DecompilerContext.startMethod(varProc);\n\n      VarNamesCollector vc = varProc.getVarNamesCollector();\n      CounterContainer counter = DecompilerContext.getCounterContainer();\n\n      RootStatement root = null;\n\n      boolean isError = false;\n\n      try {\n        cancellationManager.checkCanceled();\n        if (mt.containsCode()) {\n          if (testMode) {\n            root = MethodProcessorRunnable.codeToJava(classStruct, mt, md, varProc);\n          }\n          else {\n            DecompilerContext context = DecompilerContext.getCurrentContext();\n            try {\n              cancellationManager.startMethod(classStruct.qualifiedName, mt.getName());\n              MethodProcessorRunnable mtProc =\n                new MethodProcessorRunnable(classStruct, mt, md, varProc, DecompilerContext.getCurrentContext());\n              mtProc.run();\n              cancellationManager.checkCanceled();\n              root = mtProc.getResult();\n            }\n            finally {\n              DecompilerContext.setCurrentContext(context);\n              cancellationManager.finishMethod(classStruct.qualifiedName, mt.getName());\n            }\n          }\n        }\n        else {\n          int varIndex = 0;\n          if (!mt.hasModifier(CodeConstants.ACC_STATIC)) {\n            varProc.getThisVars().put(new VarVersionPair(0, 0), classStruct.qualifiedName);\n            varProc.setVarName(new VarVersionPair(0, 0), vc.getFreeName(0));\n            varIndex = 1;\n          }\n          for (int i = 0; i < md.params.length; i++) {\n            varProc.setVarName(new VarVersionPair(varIndex, 0), vc.getFreeName(varIndex));\n            varIndex += md.params[i].getStackSize();\n          }\n        }\n      }\n      catch (CancellationManager.TimeExceedException e) {\n        String message = \"Processing time limit exceeded for method \" + mt.getName() + \", execution interrupted.\";\n        DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR);\n        isError = true;\n      }\n      catch (CancellationManager.CanceledException e) {\n        throw e;\n      }\n      catch (Throwable t) {\n        String message = \"Method \" + mt.getName() + \" \" + mt.getDescriptor() + \" couldn't be decompiled.\";\n        DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN, t);\n        isError = true;\n      }\n\n      MethodWrapper methodWrapper = new MethodWrapper(root, varProc, mt, counter);\n      methodWrapper.decompiledWithErrors = isError;\n\n      methods.addWithKey(methodWrapper, InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor()));\n\n      if (!isError) {\n        // rename vars so that no one has the same name as a field\n        VarNamesCollector namesCollector = new VarNamesCollector();\n        classStruct.getFields().forEach(f -> namesCollector.addName(f.getName()));\n        varProc.refreshVarNames(namesCollector);\n\n        applyParameterNames(mt, md, varProc);  // if parameter names are present and should be used\n\n        applyDebugInfo(mt, varProc, methodWrapper);  // if debug information is present and should be used\n      }\n\n      DecompilerContext.getLogger().endMethod();\n    }\n\n    DecompilerContext.getLogger().endClass();\n  }\n\n  private static void applyParameterNames(StructMethod mt, MethodDescriptor md, VarProcessor varProc) {\n    if (DecompilerContext.getOption(IFernflowerPreferences.USE_METHOD_PARAMETERS)) {\n      StructMethodParametersAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_METHOD_PARAMETERS);\n      if (attr != null) {\n        List<StructMethodParametersAttribute.Entry> entries = attr.getEntries();\n        int index = varProc.getFirstParameterVarIndex();\n        for (int i = varProc.getFirstParameterPosition(); i < entries.size(); i++) {\n          StructMethodParametersAttribute.Entry entry = entries.get(i);\n          if (entry.myName != null) {\n            varProc.setVarName(new VarVersionPair(index, 0), entry.myName);\n          }\n          if ((entry.myAccessFlags & CodeConstants.ACC_FINAL) != 0) {\n            varProc.setParameterFinal(new VarVersionPair(index, 0));\n          }\n          index += md.params[i].getStackSize();\n        }\n      }\n    }\n  }\n\n  private static void applyDebugInfo(StructMethod mt, VarProcessor varProc, MethodWrapper methodWrapper) {\n    if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {\n      StructLocalVariableTableAttribute attr = mt.getLocalVariableAttr();\n      if (attr != null) {\n        // only param names here\n        varProc.setDebugVarNames(attr.getMapParamNames());\n\n        // the rest is here\n        methodWrapper.getOrBuildGraph().iterateExprents(exprent -> {\n          List<Exprent> lst = exprent.getAllExprents(true);\n          lst.add(exprent);\n          lst.stream()\n            .filter(e -> e.type == Exprent.EXPRENT_VAR)\n            .forEach(e -> {\n              VarExprent varExprent = (VarExprent)e;\n              String name = varExprent.getDebugName(mt);\n              if (name != null) {\n                varProc.setVarName(varExprent.getVarVersionPair(), name);\n              }\n            });\n          return 0;\n        });\n      }\n    }\n  }\n\n  public MethodWrapper getMethodWrapper(String name, String descriptor) {\n    return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));\n  }\n\n  public StructClass getClassStruct() {\n    return classStruct;\n  }\n\n  public VBStyleCollection<MethodWrapper, String> getMethods() {\n    return methods;\n  }\n\n  public Set<String> getHiddenMembers() {\n    return hiddenMembers;\n  }\n\n  public VBStyleCollection<Exprent, String> getStaticFieldInitializers() {\n    return staticFieldInitializers;\n  }\n\n  public VBStyleCollection<Exprent, String> getDynamicFieldInitializers() {\n    return dynamicFieldInitializers;\n  }\n\n  @Override\n  public String toString() {\n    return classStruct.qualifiedName;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.main.rels;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.attr.StructBootstrapMethodsAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.LinkConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PooledConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.io.IOException;\nimport java.util.BitSet;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class LambdaProcessor {\n  @SuppressWarnings(\"SpellCheckingInspection\") private static final String JAVAC_LAMBDA_CLASS = \"java/lang/invoke/LambdaMetafactory\";\n  @SuppressWarnings(\"SpellCheckingInspection\") private static final String JAVAC_LAMBDA_METHOD = \"metafactory\";\n  @SuppressWarnings(\"SpellCheckingInspection\") private static final String JAVAC_LAMBDA_ALT_METHOD = \"altMetafactory\";\n\n  public void processClass(ClassNode node) throws IOException {\n    for (ClassNode child : node.nested) {\n      processClass(child);\n    }\n\n    ClassesProcessor clProcessor = DecompilerContext.getClassProcessor();\n    StructClass cl = node.classStruct;\n\n    if (!cl.isVersion8()) { // lambda beginning with Java 8\n      return;\n    }\n\n    StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);\n    if (bootstrap == null || bootstrap.getMethodsNumber() == 0) {\n      return; // no bootstrap constants in pool\n    }\n\n    BitSet lambdaMethods = new BitSet();\n\n    // find lambda bootstrap constants\n    for (int i = 0; i < bootstrap.getMethodsNumber(); ++i) {\n      LinkConstant method_ref = bootstrap.getMethodReference(i); // method handle\n\n      // FIXME: extend for Eclipse etc. at some point\n      if (JAVAC_LAMBDA_CLASS.equals(method_ref.className) &&\n          (JAVAC_LAMBDA_METHOD.equals(method_ref.elementName) || JAVAC_LAMBDA_ALT_METHOD.equals(method_ref.elementName))) {\n        lambdaMethods.set(i);\n      }\n    }\n\n    if (lambdaMethods.isEmpty()) {\n      return; // no lambda bootstrap constant found\n    }\n\n    Map<String, String> mapMethodsLambda = new HashMap<>();\n\n    // iterate over code and find invocations of bootstrap methods. Replace them with anonymous classes.\n    for (StructMethod mt : cl.getMethods()) {\n      mt.expandData(cl);\n\n      InstructionSequence seq = mt.getInstructionSequence();\n      if (seq != null && seq.length() > 0) {\n        int len = seq.length();\n\n        for (int i = 0; i < len; ++i) {\n          Instruction instr = seq.getInstr(i);\n\n          if (instr.opcode == CodeConstants.opc_invokedynamic) {\n            LinkConstant invoke_dynamic = cl.getPool().getLinkConstant(instr.operand(0));\n\n            if (lambdaMethods.get(invoke_dynamic.index1)) { // lambda invocation found\n\n              List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_dynamic.index1);\n              MethodDescriptor md = MethodDescriptor.parseDescriptor(invoke_dynamic.descriptor);\n\n              String lambda_class_name = md.ret.getValue();\n              String lambda_method_name = invoke_dynamic.elementName;\n              String lambda_method_descriptor = ((PrimitiveConstant)bootstrap_arguments.get(2)).getString(); // method type\n\n              LinkConstant content_method_handle = (LinkConstant)bootstrap_arguments.get(1);\n\n              ClassNode node_lambda = new ClassNode(content_method_handle.className, content_method_handle.elementName,\n                                                    content_method_handle.descriptor, content_method_handle.index1,\n                                                    lambda_class_name, lambda_method_name, lambda_method_descriptor, cl);\n              node_lambda.simpleName = cl.qualifiedName + \"##Lambda_\" + invoke_dynamic.index1 + \"_\" + invoke_dynamic.index2;\n              node_lambda.enclosingMethod = InterpreterUtil.makeUniqueKey(mt.getName(), mt.getDescriptor());\n\n              node.nested.add(node_lambda);\n              node_lambda.parent = node;\n\n              clProcessor.getMapRootClasses().put(node_lambda.simpleName, node_lambda);\n              if (!node_lambda.lambdaInformation.is_method_reference) {\n                mapMethodsLambda.put(node_lambda.lambdaInformation.content_method_key, node_lambda.simpleName);\n              }\n            }\n          }\n        }\n      }\n\n      mt.releaseResources();\n    }\n\n    // build class hierarchy on lambda\n    for (ClassNode nd : node.nested) {\n      if (nd.type == ClassNode.CLASS_LAMBDA) {\n        String parent_class_name = mapMethodsLambda.get(nd.enclosingMethod);\n        if (parent_class_name != null) {\n          ClassNode parent_class = clProcessor.getMapRootClasses().get(parent_class_name);\n\n          parent_class.nested.add(nd);\n          nd.parent = parent_class;\n        }\n      }\n    }\n\n    // FIXME: mixed hierarchy?\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.rels;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.ExceptionDeobfuscator;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\n\nimport java.io.IOException;\n\npublic class MethodProcessorRunnable implements Runnable {\n  public final Object lock = new Object();\n\n  private final StructClass klass;\n  private final StructMethod method;\n  private final MethodDescriptor methodDescriptor;\n  private final VarProcessor varProc;\n  private final DecompilerContext parentContext;\n\n  private volatile RootStatement root;\n  private volatile Throwable error;\n  private volatile boolean finished = false;\n\n  public MethodProcessorRunnable(StructClass klass,\n                                 StructMethod method,\n                                 MethodDescriptor methodDescriptor,\n                                 VarProcessor varProc,\n                                 DecompilerContext parentContext) {\n    this.klass = klass;\n    this.method = method;\n    this.methodDescriptor = methodDescriptor;\n    this.varProc = varProc;\n    this.parentContext = parentContext;\n  }\n\n  @Override\n  public void run() {\n    error = null;\n    root = null;\n\n    try {\n      DecompilerContext.setCurrentContext(parentContext);\n      root = codeToJava(klass, method, methodDescriptor, varProc);\n    }\n    catch (Throwable t) {\n      error = t;\n    }\n    finally {\n      DecompilerContext.setCurrentContext(null);\n    }\n\n    finished = true;\n    synchronized (lock) {\n      lock.notifyAll();\n    }\n  }\n\n  public static RootStatement codeToJava(StructClass cl, StructMethod mt, MethodDescriptor md, VarProcessor varProc) throws IOException {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n    cancellationManager.checkCanceled();\n    boolean isInitializer = CodeConstants.CLINIT_NAME.equals(mt.getName()); // for now static initializer only\n\n    mt.expandData(cl);\n    InstructionSequence seq = mt.getInstructionSequence();\n    ControlFlowGraph graph = new ControlFlowGraph(seq);\n\n    DeadCodeHelper.removeDeadBlocks(graph);\n\n    //\n    // According to the JVMS11 4.9.1:\n    //   If the class file version number is 51.0 or above,\n    //   then neither the jsr opcode or the jsr_w opcode may appear in the code array\n    //\n    // Since jsr instruction is forbidden for class files version 51.0 (Java 7) or above\n    // call to inlineJsr() is only meaningful for class files prior to the Java 7.\n    //\n    cancellationManager.checkCanceled();\n    if (!cl.isVersion7()) {\n      graph.inlineJsr(cl, mt);\n    }\n\n    // TODO: move to the start, before jsr inlining\n    DeadCodeHelper.connectDummyExitBlock(graph);\n\n    DeadCodeHelper.removeGoTos(graph);\n\n    ExceptionDeobfuscator.duplicateMergedCatchBlocks(graph, cl);\n\n    ExceptionDeobfuscator.removeCircularRanges(graph);\n\n    ExceptionDeobfuscator.restorePopRanges(graph);\n\n    if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {\n      ExceptionDeobfuscator.removeEmptyRanges(graph);\n    }\n\n    if (DecompilerContext.getOption(IFernflowerPreferences.ENSURE_SYNCHRONIZED_MONITOR)) {\n      // special case: search for 'synchronized' ranges w/o monitorexit instruction (as generated by Kotlin and Scala)\n      DeadCodeHelper.extendSynchronizedRangeToMonitorExit(graph);\n    }\n\n    if (DecompilerContext.getOption(IFernflowerPreferences.NO_EXCEPTIONS_RETURN)) {\n      // special case: single return instruction outside of a protected range\n      DeadCodeHelper.incorporateValueReturns(graph);\n    }\n\n    //\t\tExceptionDeobfuscator.restorePopRanges(graph);\n    ExceptionDeobfuscator.insertEmptyExceptionHandlerBlocks(graph);\n\n    DeadCodeHelper.mergeBasicBlocks(graph);\n\n    DecompilerContext.getCounterContainer().setCounter(CounterContainer.VAR_COUNTER, mt.getLocalVariables());\n\n    if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {\n      DecompilerContext.getLogger().writeMessage(\"Heavily obfuscated exception ranges found!\", IFernflowerLogger.Severity.WARN);\n      if (!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) {\n        DecompilerContext.getLogger().writeMessage(\"Found multiple entry exception ranges which could not be splitted\", IFernflowerLogger.Severity.WARN);\n      }\n      ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, mt.getBytecodeVersion());\n    }\n    cancellationManager.checkCanceled();\n    RootStatement root = DomHelper.parseGraph(graph);\n\n    cancellationManager.checkCanceled();\n\n    FinallyProcessor fProc = new FinallyProcessor(md, varProc);\n    while (fProc.iterateGraph(cl, mt, root, graph)) {\n      cancellationManager.checkCanceled();\n      root = DomHelper.parseGraph(graph);\n    }\n\n    // remove synchronized exception handler\n    // not until now because of comparison between synchronized statements in the finally cycle\n    DomHelper.removeSynchronizedHandler(root);\n    cancellationManager.checkCanceled();\n\n    //\t\tLabelHelper.lowContinueLabels(root, new HashSet<StatEdge>());\n\n    SequenceHelper.condenseSequences(root);\n\n    ClearStructHelper.clearStatements(root);\n    cancellationManager.checkCanceled();\n\n    ExprProcessor proc = new ExprProcessor(md, varProc);\n    proc.processStatement(root, cl);\n    cancellationManager.checkCanceled();\n\n    SequenceHelper.condenseSequences(root);\n\n    do {\n      StackVarsProcessor.simplifyStackVars(root, mt, cl);\n      varProc.setVarVersions(root);\n    }\n    while (new PPandMMHelper().findPPandMM(root));\n\n    cancellationManager.checkCanceled();\n\n    while (true) {\n      LabelHelper.cleanUpEdges(root);\n\n      do {\n        MergeHelper.enhanceLoops(root);\n      }\n      while (LoopExtractHelper.extractLoops(root) || IfHelper.mergeAllIfs(root));\n\n      if (DecompilerContext.getOption(IFernflowerPreferences.IDEA_NOT_NULL_ANNOTATION)) {\n        if (IdeaNotNullHelper.removeHardcodedChecks(root, mt)) {\n          SequenceHelper.condenseSequences(root);\n          StackVarsProcessor.simplifyStackVars(root, mt, cl);\n          varProc.setVarVersions(root);\n        }\n      }\n\n      LabelHelper.identifyLabels(root);\n\n      if (InlineSingleBlockHelper.inlineSingleBlocks(root)) {\n        continue;\n      }\n\n      // initializer may have at most one return point, so no transformation of method exits permitted\n      if (isInitializer || !ExitHelper.condenseExits(root)) {\n        break;\n      }\n\n      // FIXME: !!\n      //if(!EliminateLoopsHelper.eliminateLoops(root)) {\n      //  break;\n      //}\n    }\n    cancellationManager.checkCanceled();\n\n    ExitHelper.removeRedundantReturns(root);\n\n    SecondaryFunctionsHelper.identifySecondaryFunctions(root, varProc);\n\n    varProc.setVarDefinitions(root);\n\n    cancellationManager.checkCanceled();\n\n    // must be the last invocation, because it makes the statement structure inconsistent\n    // FIXME: new edge type needed\n    LabelHelper.replaceContinueWithBreak(root);\n\n    SwitchHelper.simplifySwitchesOnReferences(root, cl);\n    SwitchHelper.prepareForRules(root, cl);\n    PatternHelper.replaceAssignmentsWithPatternVariables(root, cl);\n    cancellationManager.checkCanceled();\n\n    mt.releaseResources();\n\n    return root;\n  }\n\n  public RootStatement getResult() throws Throwable {\n    Throwable t = error;\n    if (t != null) throw t;\n    return root;\n  }\n\n  public boolean isFinished() {\n    return finished;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.main.rels;\n\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class MethodWrapper {\n  public final RootStatement root;\n  public final VarProcessor varproc;\n  public final StructMethod methodStruct;\n  public final CounterContainer counter;\n  public final Set<String> setOuterVarNames = new HashSet<>();\n\n  public DirectGraph graph;\n  public List<VarVersionPair> synthParameters;\n  public boolean decompiledWithErrors;\n\n  public MethodWrapper(RootStatement root, VarProcessor varproc, StructMethod methodStruct, CounterContainer counter) {\n    this.root = root;\n    this.varproc = varproc;\n    this.methodStruct = methodStruct;\n    this.counter = counter;\n  }\n\n  public DirectGraph getOrBuildGraph() {\n    if (graph == null && root != null) {\n      graph = new FlattenStatementsHelper().buildDirectGraph(root);\n    }\n    return graph;\n  }\n\n  @Override\n  public String toString() {\n    return methodStruct.getName();\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.main.rels;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class NestedClassProcessor {\n  public void processClass(ClassNode root, ClassNode node) {\n    // hide synthetic lambda content methods\n    if (node.type == ClassNode.CLASS_LAMBDA && !node.lambdaInformation.is_method_reference) {\n      ClassNode node_content = DecompilerContext.getClassProcessor().getMapRootClasses().get(node.classStruct.qualifiedName);\n      if (node_content != null && node_content.getWrapper() != null) {\n        node_content.getWrapper().getHiddenMembers().add(node.lambdaInformation.content_method_key);\n      }\n    }\n\n    if (node.nested.isEmpty()) {\n      return;\n    }\n\n    if (node.type != ClassNode.CLASS_LAMBDA) {\n      computeLocalVarsAndDefinitions(node);\n\n      // for each local or anonymous class ensure not empty enclosing method\n      checkNotFoundClasses(root, node);\n    }\n\n    int nameless = 0, synthetics = 0;\n    for (ClassNode child : node.nested) {\n      StructClass cl = child.classStruct;\n      // ensure not-empty class name\n      if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) {\n        if ((child.access & CodeConstants.ACC_SYNTHETIC) != 0 || cl.isSynthetic()) {\n          child.simpleName = \"SyntheticClass_\" + (++synthetics);\n        }\n        else {\n          String message = \"Nameless local or member class \" + cl.qualifiedName + \"!\";\n          DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n          child.simpleName = \"NamelessClass_\" + (++nameless);\n        }\n      }\n    }\n\n    for (ClassNode child : node.nested) {\n      if (child.type == ClassNode.CLASS_LAMBDA) {\n        setLambdaVars(node, child);\n      }\n      else if (child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) {\n        insertLocalVars(node, child);\n\n        if (child.type == ClassNode.CLASS_LOCAL && child.enclosingMethod != null) {\n          MethodWrapper enclosingMethodWrapper = node.getWrapper().getMethods().getWithKey(child.enclosingMethod);\n          if(enclosingMethodWrapper != null) { // e.g. in case of switch-on-enum. FIXME: some proper handling of multiple enclosing classes \n            setLocalClassDefinition(enclosingMethodWrapper, child);\n          }\n        }\n      }\n    }\n\n    for (ClassNode child : node.nested) {\n      processClass(root, child);\n    }\n  }\n\n  private static void setLambdaVars(ClassNode parent, ClassNode child) {\n    if (child.lambdaInformation.is_method_reference) { // method reference, no code and no parameters\n      return;\n    }\n\n    MethodWrapper method = parent.getWrapper().getMethods().getWithKey(child.lambdaInformation.content_method_key);\n    MethodWrapper enclosingMethod = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod);\n\n    MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambdaInformation.method_descriptor);\n    MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambdaInformation.content_method_descriptor);\n\n    int vars_count = md_content.params.length - md_lambda.params.length;\n    boolean is_static_lambda_content = child.lambdaInformation.is_content_method_static;\n\n    String parent_class_name = parent.getWrapper().getClassStruct().qualifiedName;\n    String lambda_class_name = child.simpleName;\n\n    VarType lambda_class_type = new VarType(lambda_class_name, true);\n\n    // this pointer\n    if (!is_static_lambda_content && DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) {\n      method.varproc.getThisVars().put(new VarVersionPair(0, 0), parent_class_name);\n      method.varproc.setVarName(new VarVersionPair(0, 0), parent.simpleName + \".this\");\n    }\n\n    Map<VarVersionPair, String> mapNewNames = new HashMap<>();\n\n    enclosingMethod.getOrBuildGraph().iterateExprents(exprent -> {\n      List<Exprent> lst = exprent.getAllExprents(true);\n      lst.add(exprent);\n\n      for (Exprent expr : lst) {\n        if (expr.type == Exprent.EXPRENT_NEW) {\n          NewExprent new_expr = (NewExprent)expr;\n\n          VarNamesCollector enclosingCollector = new VarNamesCollector(enclosingMethod.varproc.getVarNames());\n\n          if (new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewType())) {\n            InvocationExprent inv_dynamic = new_expr.getConstructor();\n\n            int param_index = is_static_lambda_content ? 0 : 1;\n            int varIndex = is_static_lambda_content ? 0 : 1;\n\n            for (int i = 0; i < md_content.params.length; ++i) {\n              VarVersionPair varVersion = new VarVersionPair(varIndex, 0);\n              if (i < vars_count) {\n                Exprent param = inv_dynamic.getParameters().get(param_index + i);\n\n                if (param.type == Exprent.EXPRENT_VAR) {\n                  mapNewNames.put(varVersion, enclosingMethod.varproc.getVarName(new VarVersionPair((VarExprent)param)));\n                }\n              }\n              else {\n                mapNewNames.put(varVersion, enclosingCollector.getFreeName(method.varproc.getVarName(varVersion)));\n              }\n\n              varIndex += md_content.params[i].getStackSize();\n            }\n          }\n        }\n      }\n\n      return 0;\n    });\n\n    // update names of local variables\n    Set<String> setNewOuterNames = new HashSet<>(mapNewNames.values());\n    setNewOuterNames.removeAll(method.setOuterVarNames);\n\n    method.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames));\n    method.setOuterVarNames.addAll(setNewOuterNames);\n\n    for (Entry<VarVersionPair, String> entry : mapNewNames.entrySet()) {\n      method.varproc.setVarName(entry.getKey(), entry.getValue());\n    }\n  }\n\n  private static void checkNotFoundClasses(ClassNode root, ClassNode node) {\n    List<ClassNode> copy = new ArrayList<>(node.nested);\n\n    for (ClassNode child : copy) {\n      if (child.classStruct.isSynthetic()) {\n        continue;\n      }\n\n      if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) {\n        Set<String> setEnclosing = child.enclosingClasses;\n\n        if (!setEnclosing.isEmpty()) {\n          StructEnclosingMethodAttribute attr = child.classStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_ENCLOSING_METHOD);\n          if (attr != null &&\n              attr.getMethodName() != null &&\n              node.classStruct.qualifiedName.equals(attr.getClassName()) &&\n              node.classStruct.getMethod(attr.getMethodName(), attr.getMethodDescriptor()) != null) {\n            child.enclosingMethod = InterpreterUtil.makeUniqueKey(attr.getMethodName(), attr.getMethodDescriptor());\n            continue;\n          }\n        }\n\n        node.nested.remove(child);\n        child.parent = null;\n        setEnclosing.remove(node.classStruct.qualifiedName);\n\n        boolean hasEnclosing = !setEnclosing.isEmpty() && insertNestedClass(root, child);\n\n        if (!hasEnclosing) {\n          if (child.type == ClassNode.CLASS_ANONYMOUS) {\n            String message = \"Unreferenced anonymous class \" + child.classStruct.qualifiedName + \"!\";\n            DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n          }\n          else if (child.type == ClassNode.CLASS_LOCAL) {\n            String message = \"Unreferenced local class \" + child.classStruct.qualifiedName + \"!\";\n            DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n          }\n        }\n      }\n    }\n  }\n\n  private static boolean insertNestedClass(ClassNode root, ClassNode child) {\n    Set<String> setEnclosing = child.enclosingClasses;\n\n    LinkedList<ClassNode> stack = new LinkedList<>();\n    stack.add(root);\n\n    while (!stack.isEmpty()) {\n      ClassNode node = stack.removeFirst();\n\n      if (setEnclosing.contains(node.classStruct.qualifiedName)) {\n        node.nested.add(child);\n        child.parent = node;\n\n        return true;\n      }\n\n      // note: ordered list\n      stack.addAll(node.nested);\n    }\n\n    return false;\n  }\n\n  private static void computeLocalVarsAndDefinitions(ClassNode node) {\n    // class name -> constructor descriptor -> var to field link\n    Map<String, Map<String, List<VarFieldPair>>> mapVarMasks = new HashMap<>();\n\n    int clTypes = 0;\n\n    for (ClassNode nd : node.nested) {\n      if (nd.type != ClassNode.CLASS_LAMBDA &&\n          !nd.classStruct.isSynthetic() &&\n          (nd.access & CodeConstants.ACC_STATIC) == 0 &&\n          (nd.access & CodeConstants.ACC_INTERFACE) == 0) {\n        clTypes |= nd.type;\n\n        Map<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.getWrapper());\n        if (mask.isEmpty()) {\n          String message = \"Nested class \" + nd.classStruct.qualifiedName + \" has no constructor!\";\n          DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);\n        }\n        else {\n          mapVarMasks.put(nd.classStruct.qualifiedName, mask);\n        }\n      }\n    }\n\n    // local var masks\n    Map<String, Map<String, List<VarFieldPair>>> mapVarFieldPairs = new HashMap<>();\n\n    if (clTypes != ClassNode.CLASS_MEMBER) {\n      // iterate enclosing class\n      for (MethodWrapper method : node.getWrapper().getMethods()) {\n        if (method.root != null) { // neither abstract, nor native\n          method.getOrBuildGraph().iterateExprents(exprent -> {\n            List<Exprent> lst = exprent.getAllExprents(true);\n            lst.add(exprent);\n\n            for (Exprent expr : lst) {\n              if (expr.type == Exprent.EXPRENT_NEW) {\n                InvocationExprent constructor = ((NewExprent)expr).getConstructor();\n\n                if (constructor != null && mapVarMasks.containsKey(constructor.getClassName())) { // non-static inner class constructor\n                  String refClassName = constructor.getClassName();\n                  ClassNode nestedClassNode = node.getClassNode(refClassName);\n\n                  if (nestedClassNode.type != ClassNode.CLASS_MEMBER) {\n                    List<VarFieldPair> mask = mapVarMasks.get(refClassName).get(constructor.getStringDescriptor());\n\n                    if (!mapVarFieldPairs.containsKey(refClassName)) {\n                      mapVarFieldPairs.put(refClassName, new HashMap<>());\n                    }\n\n                    List<VarFieldPair> lstTemp = new ArrayList<>();\n\n                    for (int i = 0; i < mask.size(); i++) {\n                      Exprent param = constructor.getParameters().get(i);\n                      VarFieldPair pair = null;\n\n                      if (param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) {\n                        VarVersionPair varPair = new VarVersionPair((VarExprent)param);\n\n                        // FIXME: flags of variables are wrong! Correct the entire functionality.\n                        // if(method.varproc.getVarFinal(varPair) != VarTypeProcessor.VAR_NON_FINAL) {\n                        pair = new VarFieldPair(mask.get(i).fieldKey, varPair);\n                        // }\n                      }\n\n                      lstTemp.add(pair);\n                    }\n\n                    List<VarFieldPair> pairMask = mapVarFieldPairs.get(refClassName).get(constructor.getStringDescriptor());\n                    if (pairMask == null) {\n                      pairMask = lstTemp;\n                    }\n                    else {\n                      for (int i = 0; i < pairMask.size(); i++) {\n                        if (!Objects.equals(pairMask.get(i), lstTemp.get(i))) {\n                          pairMask.set(i, null);\n                        }\n                      }\n                    }\n\n                    mapVarFieldPairs.get(refClassName).put(constructor.getStringDescriptor(), pairMask);\n                    nestedClassNode.enclosingMethod =\n                      InterpreterUtil.makeUniqueKey(method.methodStruct.getName(), method.methodStruct.getDescriptor());\n                  }\n                }\n              }\n            }\n\n            return 0;\n          });\n        }\n      }\n    }\n\n    // merge var masks\n    for (Entry<String, Map<String, List<VarFieldPair>>> enclosing : mapVarMasks.entrySet()) {\n      ClassNode nestedNode = node.getClassNode(enclosing.getKey());\n\n      // intersection\n      List<VarFieldPair> interPairMask = null;\n      // merge referenced constructors\n      if (mapVarFieldPairs.containsKey(enclosing.getKey())) {\n        for (List<VarFieldPair> mask : mapVarFieldPairs.get(enclosing.getKey()).values()) {\n          if (interPairMask == null) {\n            interPairMask = new ArrayList<>(mask);\n          }\n          else {\n            mergeListSignatures(interPairMask, mask, false);\n          }\n        }\n      }\n\n      List<VarFieldPair> interMask = null;\n      // merge all constructors\n      for (List<VarFieldPair> mask : enclosing.getValue().values()) {\n        if (interMask == null) {\n          interMask = new ArrayList<>(mask);\n        }\n        else {\n          mergeListSignatures(interMask, mask, false);\n        }\n      }\n\n      if (interPairMask == null) { // member or local and never instantiated\n        interPairMask = interMask != null ? new ArrayList<>(interMask) : new ArrayList<>();\n\n        boolean found = false;\n\n        for (int i = 0; i < interPairMask.size(); i++) {\n          if (interPairMask.get(i) != null) {\n            if (found) {\n              interPairMask.set(i, null);\n            }\n            found = true;\n          }\n        }\n      }\n\n      mergeListSignatures(interPairMask, interMask, true);\n\n      for (VarFieldPair pair : interPairMask) {\n        if (pair != null && !pair.fieldKey.isEmpty()) {\n          nestedNode.mapFieldsToVars.put(pair.fieldKey, pair.varPair);\n        }\n      }\n\n      // set resulting constructor signatures\n      for (Entry<String, List<VarFieldPair>> entry : enclosing.getValue().entrySet()) {\n        mergeListSignatures(entry.getValue(), interPairMask, false);\n\n        List<VarVersionPair> mask = new ArrayList<>(entry.getValue().size());\n        for (VarFieldPair pair : entry.getValue()) {\n          mask.add(pair != null && !pair.fieldKey.isEmpty() ? pair.varPair : null);\n        }\n        nestedNode.getWrapper().getMethodWrapper(CodeConstants.INIT_NAME, entry.getKey()).synthParameters = mask;\n      }\n    }\n  }\n\n  private static void insertLocalVars(ClassNode parent, ClassNode child) {\n    // enclosing method, is null iff member class\n    MethodWrapper enclosingMethod = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod);\n\n    // iterate all child methods\n    for (MethodWrapper method : child.getWrapper().getMethods()) {\n      if (method.root != null) { // neither abstract nor native\n        Map<VarVersionPair, String> mapNewNames = new HashMap<>();  // local var names\n        Map<VarVersionPair, VarType> mapNewTypes = new HashMap<>();  // local var types\n\n        Map<Integer, VarVersionPair> mapParamsToNewVars = new HashMap<>();\n        if (method.synthParameters != null) {\n          int index = 0, varIndex = 1;\n          MethodDescriptor md = MethodDescriptor.parseDescriptor(method.methodStruct.getDescriptor());\n\n          for (VarVersionPair pair : method.synthParameters) {\n            if (pair != null) {\n              VarVersionPair newVar = new VarVersionPair(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0);\n\n              mapParamsToNewVars.put(varIndex, newVar);\n\n              String varName = null;\n              VarType varType = null;\n\n              if (child.type != ClassNode.CLASS_MEMBER) {\n                varName = enclosingMethod.varproc.getVarName(pair);\n                varType = enclosingMethod.varproc.getVarType(pair);\n\n                enclosingMethod.varproc.setVarFinal(pair, VarProcessor.VAR_EXPLICIT_FINAL);\n              }\n\n              if (pair.var == -1 || \"this\".equals(varName)) {\n                if (parent.simpleName == null) {\n                  // anonymous enclosing class, no access to this\n                  varName = VarExprent.VAR_NAMELESS_ENCLOSURE;\n                }\n                else {\n                  varName = parent.simpleName + \".this\";\n                }\n                method.varproc.getThisVars().put(newVar, parent.classStruct.qualifiedName);\n              }\n\n              mapNewNames.put(newVar, varName);\n              mapNewTypes.put(newVar, varType);\n            }\n\n            varIndex += md.params[index++].getStackSize();\n          }\n        }\n\n        Map<String, VarVersionPair> mapFieldsToNewVars = new HashMap<>();\n        for (ClassNode classNode = child; classNode != null; classNode = classNode.parent) {\n          for (Entry<String, VarVersionPair> entry : classNode.mapFieldsToVars.entrySet()) {\n            VarVersionPair newVar = new VarVersionPair(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0);\n\n            mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(classNode.classStruct.qualifiedName, entry.getKey()), newVar);\n\n            String varName = null;\n            VarType varType = null;\n\n            if (classNode.type != ClassNode.CLASS_MEMBER) {\n              MethodWrapper enclosing_method = classNode.parent.getWrapper().getMethods().getWithKey(classNode.enclosingMethod);\n\n              varName = enclosing_method.varproc.getVarName(entry.getValue());\n              varType = enclosing_method.varproc.getVarType(entry.getValue());\n\n              enclosing_method.varproc.setVarFinal(entry.getValue(), VarProcessor.VAR_EXPLICIT_FINAL);\n            }\n\n            if (entry.getValue().var == -1 || \"this\".equals(varName)) {\n              if (classNode.parent.simpleName == null) {\n                // anonymous enclosing class, no access to this\n                varName = VarExprent.VAR_NAMELESS_ENCLOSURE;\n              }\n              else {\n                varName = classNode.parent.simpleName + \".this\";\n              }\n              method.varproc.getThisVars().put(newVar, classNode.parent.classStruct.qualifiedName);\n            }\n\n            mapNewNames.put(newVar, varName);\n            mapNewTypes.put(newVar, varType);\n\n            // hide synthetic field\n            if (classNode == child) { // fields higher up the chain were already handled with their classes\n              StructField fd = child.classStruct.getFields().getWithKey(entry.getKey());\n              child.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));\n            }\n          }\n        }\n\n        Set<String> setNewOuterNames = new HashSet<>(mapNewNames.values());\n        setNewOuterNames.removeAll(method.setOuterVarNames);\n\n        method.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames));\n        method.setOuterVarNames.addAll(setNewOuterNames);\n\n        for (Entry<VarVersionPair, String> entry : mapNewNames.entrySet()) {\n          VarVersionPair pair = entry.getKey();\n          VarType type = mapNewTypes.get(pair);\n\n          method.varproc.setVarName(pair, entry.getValue());\n          if (type != null) {\n            method.varproc.setVarType(pair, type);\n          }\n        }\n\n        method.getOrBuildGraph().iterateExprents(new DirectGraph.ExprentIterator() {\n          @Override\n          public int processExprent(Exprent exprent) {\n            if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n              AssignmentExprent assignExpr = (AssignmentExprent)exprent;\n              if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {\n                FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();\n                String qName = child.classStruct.qualifiedName;\n                if (fExpr.getClassname().equals(qName) &&  // process this class only\n                    mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(qName, fExpr.getName(), fExpr.getDescriptor().descriptorString))) {\n                  return 2;\n                }\n              }\n            }\n\n            if (child.type == ClassNode.CLASS_ANONYMOUS &&\n                CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) &&\n                exprent.type == Exprent.EXPRENT_INVOCATION) {\n              InvocationExprent invokeExpr = (InvocationExprent)exprent;\n              if (invokeExpr.getFuncType() == InvocationExprent.TYPE_INIT) {\n                // invocation of the super constructor in an anonymous class\n                child.superInvocation = invokeExpr; // FIXME: save original names of parameters\n                return 2;\n              }\n            }\n\n            replaceExprent(exprent);\n\n            return 0;\n          }\n\n          private Exprent replaceExprent(Exprent exprent) {\n            if (exprent.type == Exprent.EXPRENT_VAR) {\n              int varIndex = ((VarExprent)exprent).getIndex();\n              if (mapParamsToNewVars.containsKey(varIndex)) {\n                VarVersionPair newVar = mapParamsToNewVars.get(varIndex);\n                method.varproc.getExternalVars().add(newVar);\n                return new VarExprent(newVar.var, method.varproc.getVarType(newVar), method.varproc);\n              }\n            }\n            else if (exprent.type == Exprent.EXPRENT_FIELD) {\n              FieldExprent fExpr = (FieldExprent)exprent;\n              String key = InterpreterUtil.makeUniqueKey(fExpr.getClassname(), fExpr.getName(), fExpr.getDescriptor().descriptorString);\n              if (mapFieldsToNewVars.containsKey(key)) {\n                //if(fExpr.getClassname().equals(child.classStruct.qualifiedName) &&\n                //\t\tmapFieldsToNewVars.containsKey(key)) {\n                VarVersionPair newVar = mapFieldsToNewVars.get(key);\n                method.varproc.getExternalVars().add(newVar);\n                return new VarExprent(newVar.var, method.varproc.getVarType(newVar), method.varproc);\n              }\n            }\n\n            boolean replaced = true;\n            while (replaced) {\n              replaced = false;\n\n              for (Exprent expr : exprent.getAllExprents()) {\n                Exprent retExpr = replaceExprent(expr);\n                if (retExpr != null) {\n                  exprent.replaceExprent(expr, retExpr);\n                  replaced = true;\n                  break;\n                }\n              }\n            }\n\n            return null;\n          }\n        });\n      }\n    }\n  }\n\n  private static Map<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wrapper) {\n    Map<String, List<VarFieldPair>> mapMasks = new HashMap<>();\n\n    StructClass cl = wrapper.getClassStruct();\n\n    // iterate over constructors\n    for (StructMethod mt : cl.getMethods()) {\n      if (CodeConstants.INIT_NAME.equals(mt.getName())) {\n        MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n        MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.INIT_NAME, mt.getDescriptor());\n        DirectGraph graph = method.getOrBuildGraph();\n\n        if (graph != null) { // something gone wrong, should not be null\n          List<VarFieldPair> fields = new ArrayList<>(md.params.length);\n\n          int varIndex = 1;\n          for (int i = 0; i < md.params.length; i++) {  // no static methods allowed\n            String keyField = getEnclosingVarField(cl, method, graph, varIndex);\n            fields.add(keyField == null ? null : new VarFieldPair(keyField, new VarVersionPair(-1, 0))); // TODO: null?\n            varIndex += md.params[i].getStackSize();\n          }\n\n          mapMasks.put(mt.getDescriptor(), fields);\n        }\n      }\n    }\n\n    return mapMasks;\n  }\n\n  private static String getEnclosingVarField(StructClass cl, MethodWrapper method, DirectGraph graph, int index) {\n    String field = \"\";\n\n    // parameter variable final\n    if (method.varproc.getVarFinal(new VarVersionPair(index, 0)) == VarProcessor.VAR_NON_FINAL) {\n      return null;\n    }\n\n    boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);\n\n    // no loop at the begin\n    DirectNode firstNode = graph.first;\n    if (firstNode.predecessors.isEmpty()) {\n      // assignment to a synthetic field?\n      for (Exprent exprent : firstNode.exprents) {\n        if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n          AssignmentExprent assignExpr = (AssignmentExprent)exprent;\n          if (assignExpr.getRight().type == Exprent.EXPRENT_VAR &&\n              ((VarExprent)assignExpr.getRight()).getIndex() == index &&\n              assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {\n            FieldExprent left = (FieldExprent)assignExpr.getLeft();\n            StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString);\n            if (fd != null &&\n                cl.qualifiedName.equals(left.getClassname()) &&\n                (fd.isSynthetic() || noSynthFlag && possiblySyntheticField(fd))) {\n              // local (== not inherited) field\n              field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString);\n              break;\n            }\n          }\n        }\n      }\n    }\n\n    return field;\n  }\n\n  private static boolean possiblySyntheticField(StructField fd) {\n    return fd.getName().contains(\"$\") && fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_PRIVATE);\n  }\n\n  private static void mergeListSignatures(List<VarFieldPair> first, List<VarFieldPair> second, boolean both) {\n    int i = 1;\n\n    while (first.size() > i && second.size() > i) {\n      VarFieldPair fObj = first.get(first.size() - i);\n      VarFieldPair sObj = second.get(second.size() - i);\n\n      if (!isEqual(both, fObj, sObj)) {\n        first.set(first.size() - i, null);\n        if (both) {\n          second.set(second.size() - i, null);\n        }\n      }\n      else if (fObj != null) {\n        if (fObj.varPair.var == -1) {\n          fObj.varPair = sObj.varPair;\n        }\n        else {\n          sObj.varPair = fObj.varPair;\n        }\n      }\n\n      i++;\n    }\n\n    for (int j = 1; j <= first.size() - i; j++) {\n      first.set(j, null);\n    }\n\n    if (both) {\n      for (int j = 1; j <= second.size() - i; j++) {\n        second.set(j, null);\n      }\n    }\n\n    // first\n    if (first.isEmpty()) {\n      if (!second.isEmpty() && both) {\n        second.set(0, null);\n      }\n    }\n    else if (second.isEmpty()) {\n      first.set(0, null);\n    }\n    else {\n      VarFieldPair fObj = first.get(0);\n      VarFieldPair sObj = second.get(0);\n\n      if (!isEqual(both, fObj, sObj)) {\n        first.set(0, null);\n        if (both) {\n          second.set(0, null);\n        }\n      }\n      else if (fObj != null) {\n        if (fObj.varPair.var == -1) {\n          fObj.varPair = sObj.varPair;\n        }\n        else {\n          sObj.varPair = fObj.varPair;\n        }\n      }\n    }\n  }\n\n  private static boolean isEqual(boolean both, VarFieldPair fObj, VarFieldPair sObj) {\n    boolean eq;\n    if (fObj == null || sObj == null) {\n      eq = (fObj == sObj);\n    }\n    else {\n      eq = true;\n      if (fObj.fieldKey.length() == 0) {\n        fObj.fieldKey = sObj.fieldKey;\n      }\n      else if (sObj.fieldKey.length() == 0) {\n        if (both) {\n          sObj.fieldKey = fObj.fieldKey;\n        }\n      }\n      else {\n        eq = fObj.fieldKey.equals(sObj.fieldKey);\n      }\n    }\n    return eq;\n  }\n\n  private static void setLocalClassDefinition(MethodWrapper method, ClassNode node) {\n    RootStatement root = method.root;\n\n    Set<Statement> setStats = new HashSet<>();\n    VarType classType = new VarType(node.classStruct.qualifiedName, true);\n\n    Statement statement = getDefStatement(root, classType, setStats);\n    if (statement == null) {\n      // unreferenced local class\n      statement = root.getFirst();\n    }\n\n    Statement first = findFirstBlock(statement, setStats);\n\n    List<Exprent> lst;\n    if (first == null) {\n      lst = statement.getVarDefinitions();\n    }\n    else if (first.getExprents() == null) {\n      lst = first.getVarDefinitions();\n    }\n    else {\n      lst = first.getExprents();\n    }\n\n    int addIndex = 0;\n    for (Exprent expr : lst) {\n      if (searchForClass(expr, classType)) {\n        break;\n      }\n      addIndex++;\n    }\n\n    VarExprent var = new VarExprent(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), classType, method.varproc);\n    var.setDefinition(true);\n    var.setClassDef(true);\n\n    lst.add(addIndex, var);\n  }\n\n  private static Statement findFirstBlock(Statement stat, Set<Statement> setStats) {\n    LinkedList<Statement> stack = new LinkedList<>();\n    stack.add(stat);\n\n    while (!stack.isEmpty()) {\n      Statement st = stack.remove(0);\n\n      if (stack.isEmpty() || setStats.contains(st)) {\n        if (st.isLabeled() && !stack.isEmpty() || st.getExprents() != null) {\n          return st;\n        }\n\n        stack.clear();\n\n        switch (st.type) {\n          case SEQUENCE -> stack.addAll(0, st.getStats());\n          case IF, ROOT, SWITCH, SYNCHRONIZED -> stack.add(st.getFirst());\n          default -> {\n            return st;\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n\n  private static Statement getDefStatement(Statement stat, VarType classType, Set<? super Statement> setStats) {\n    List<Exprent> lst = new ArrayList<>();\n    Statement retStat = null;\n\n    if (stat.getExprents() == null) {\n      int counter = 0;\n\n      for (Object obj : stat.getSequentialObjects()) {\n        if (obj instanceof Statement st) {\n\n          Statement stTemp = getDefStatement(st, classType, setStats);\n\n          if (stTemp != null) {\n            if (counter == 1) {\n              retStat = stat;\n              break;\n            }\n            retStat = stTemp;\n            counter++;\n          }\n\n          if (st.type == StatementType.DO) {\n            DoStatement dost = (DoStatement)st;\n\n            lst.addAll(dost.getInitExprentList());\n            lst.addAll(dost.getConditionExprentList());\n          }\n        }\n        else if (obj instanceof Exprent) {\n          lst.add((Exprent)obj);\n        }\n      }\n    }\n    else {\n      lst = stat.getExprents();\n    }\n\n    if (retStat != stat) {\n      for (Exprent exprent : lst) {\n        if (exprent != null && searchForClass(exprent, classType)) {\n          retStat = stat;\n          break;\n        }\n      }\n    }\n\n    if (retStat != null) {\n      setStats.add(stat);\n    }\n\n    return retStat;\n  }\n\n  private static boolean searchForClass(Exprent exprent, VarType classType) {\n    List<Exprent> lst = exprent.getAllExprents(true);\n    lst.add(exprent);\n\n    String classname = classType.getValue();\n\n    for (Exprent expr : lst) {\n      boolean res = false;\n\n      switch (expr.type) {\n        case Exprent.EXPRENT_CONST -> {\n          ConstExprent constExpr = (ConstExprent)expr;\n          res = (VarType.VARTYPE_CLASS.equals(constExpr.getConstType()) && classname.equals(constExpr.getValue()) ||\n                 classType.equals(constExpr.getConstType()));\n        }\n        case Exprent.EXPRENT_FIELD -> res = classname.equals(((FieldExprent)expr).getClassname());\n        case Exprent.EXPRENT_INVOCATION -> res = classname.equals(((InvocationExprent)expr).getClassName());\n        case Exprent.EXPRENT_NEW -> {\n          VarType newType = expr.getExprType();\n          res = newType.getType() == CodeConstants.TYPE_OBJECT && classname.equals(newType.getValue());\n        }\n        case Exprent.EXPRENT_VAR -> {\n          VarExprent varExpr = (VarExprent)expr;\n          if (varExpr.isDefinition()) {\n            VarType varType = varExpr.getVarType();\n            if (classType.equals(varType) || (varType.getArrayDim() > 0 && classType.getValue().equals(varType.getValue()))) {\n              res = true;\n            }\n          }\n        }\n      }\n\n      if (res) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  private static class VarFieldPair {\n    public String fieldKey;\n    public VarVersionPair varPair;\n\n    VarFieldPair(String field, VarVersionPair varPair) {\n      this.fieldKey = field;\n      this.varPair = varPair;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n      if (o == this) return true;\n      if (!(o instanceof VarFieldPair pair)) return false;\n\n      return fieldKey.equals(pair.fieldKey) && varPair.equals(pair.varPair);\n    }\n\n    @Override\n    public int hashCode() {\n      return fieldKey.hashCode() + varPair.hashCode();\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.main.rels;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.util.*;\n\npublic class NestedMemberAccess {\n\n  private enum MethodAccess {NORMAL, FIELD_GET, FIELD_SET, METHOD, FUNCTION}\n\n  private boolean noSynthFlag;\n  private final Map<MethodWrapper, MethodAccess> mapMethodType = new HashMap<>();\n\n\n  public void propagateMemberAccess(ClassNode root) {\n    if (root.nested.isEmpty()) {\n      return;\n    }\n\n    noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);\n\n    computeMethodTypes(root);\n\n    eliminateStaticAccess(root);\n  }\n\n\n  private void computeMethodTypes(ClassNode node) {\n    if (node.type == ClassNode.CLASS_LAMBDA) {\n      return;\n    }\n\n    for (ClassNode nd : node.nested) {\n      computeMethodTypes(nd);\n    }\n\n    for (MethodWrapper method : node.getWrapper().getMethods()) {\n      computeMethodType(node, method);\n    }\n  }\n\n  private void computeMethodType(ClassNode node, MethodWrapper method) {\n    MethodAccess type = MethodAccess.NORMAL;\n\n    if (method.root != null) {\n      DirectGraph graph = method.getOrBuildGraph();\n\n      StructMethod mt = method.methodStruct;\n      if ((noSynthFlag || mt.isSynthetic()) && mt.hasModifier(CodeConstants.ACC_STATIC)) {\n        if (graph.nodes.size() == 2) {  // incl. dummy exit node\n          if (graph.first.exprents.size() == 1) {\n            Exprent exprent = graph.first.exprents.get(0);\n\n            MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n            int parcount = mtdesc.params.length;\n\n            Exprent exprCore = exprent;\n\n            if (exprent.type == Exprent.EXPRENT_EXIT) {\n              ExitExprent exexpr = (ExitExprent)exprent;\n              if (exexpr.getExitType() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {\n                exprCore = exexpr.getValue();\n              }\n            }\n\n            switch (exprCore.type) {\n              case Exprent.EXPRENT_FIELD -> {\n                FieldExprent fexpr = (FieldExprent)exprCore;\n                if ((parcount == 1 && !fexpr.isStatic()) ||\n                    (parcount == 0 && fexpr.isStatic())) {\n                  if (fexpr.getClassname().equals(node.classStruct.qualifiedName)) {  // FIXME: check for private flag of the field\n                    if (fexpr.isStatic() ||\n                        (fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {\n                      type = MethodAccess.FIELD_GET;\n                    }\n                  }\n                }\n              }\n              case Exprent.EXPRENT_VAR -> {  // qualified this\n                if (parcount == 1) {\n                  // this or final variable\n                  if (((VarExprent)exprCore).getIndex() != 0) {\n                    type = MethodAccess.FIELD_GET;\n                  }\n                }\n              }\n              case Exprent.EXPRENT_FUNCTION -> {\n                // for now detect only increment/decrement\n                FunctionExprent functionExprent = (FunctionExprent)exprCore;\n                if (functionExprent.getFuncType() >= FunctionExprent.FUNCTION_IMM &&\n                    functionExprent.getFuncType() <= FunctionExprent.FUNCTION_PPI) {\n                  if (functionExprent.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {\n                    type = MethodAccess.FUNCTION;\n                  }\n                }\n              }\n              case Exprent.EXPRENT_INVOCATION -> type = MethodAccess.METHOD;\n              case Exprent.EXPRENT_ASSIGNMENT -> {\n                AssignmentExprent asexpr = (AssignmentExprent)exprCore;\n                if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {\n                  FieldExprent fexpras = (FieldExprent)asexpr.getLeft();\n                  if ((parcount == 2 && !fexpras.isStatic()) ||\n                      (parcount == 1 && fexpras.isStatic())) {\n                    if (fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field\n                      if (fexpras.isStatic() ||\n                          (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {\n                        if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {\n                          type = MethodAccess.FIELD_SET;\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n\n            if (type == MethodAccess.METHOD) { // FIXME: check for private flag of the method\n\n              type = MethodAccess.NORMAL;\n\n              InvocationExprent invexpr = (InvocationExprent)exprCore;\n\n              boolean isStatic = invexpr.isStatic();\n              if ((isStatic && invexpr.getParameters().size() == parcount) ||\n                  (!isStatic && invexpr.getInstance().type == Exprent.EXPRENT_VAR\n                   && ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getParameters().size() == parcount - 1)) {\n\n                boolean equalpars = true;\n\n                int index = isStatic ? 0 : 1;\n                for (int i = 0; i < invexpr.getParameters().size(); i++) {\n                  Exprent parexpr = invexpr.getParameters().get(i);\n                  if (parexpr.type != Exprent.EXPRENT_VAR || ((VarExprent)parexpr).getIndex() != index) {\n                    equalpars = false;\n                    break;\n                  }\n                  index += mtdesc.params[i + (isStatic ? 0 : 1)].getStackSize();\n                }\n\n                if (equalpars) {\n                  type = MethodAccess.METHOD;\n                }\n              }\n            }\n          }\n          else if (graph.first.exprents.size() == 2) {\n            Exprent exprentFirst = graph.first.exprents.get(0);\n            Exprent exprentSecond = graph.first.exprents.get(1);\n\n            if (exprentFirst.type == Exprent.EXPRENT_ASSIGNMENT &&\n                exprentSecond.type == Exprent.EXPRENT_EXIT) {\n\n              MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n              int parcount = mtdesc.params.length;\n\n              AssignmentExprent asexpr = (AssignmentExprent)exprentFirst;\n              if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {\n                FieldExprent fexpras = (FieldExprent)asexpr.getLeft();\n                if ((parcount == 2 && !fexpras.isStatic()) ||\n                    (parcount == 1 && fexpras.isStatic())) {\n                  if (fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field\n                    if (fexpras.isStatic() ||\n                        (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {\n                      if (((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {\n\n                        ExitExprent exexpr = (ExitExprent)exprentSecond;\n                        if (exexpr.getExitType() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {\n                          if (exexpr.getValue().type == Exprent.EXPRENT_VAR &&\n                              ((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {\n                            type = MethodAccess.FIELD_SET;\n                          }\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n\n    if (type != MethodAccess.NORMAL) {\n      mapMethodType.put(method, type);\n    }\n    else {\n      mapMethodType.remove(method);\n    }\n  }\n\n\n  private void eliminateStaticAccess(ClassNode node) {\n\n    if (node.type == ClassNode.CLASS_LAMBDA) {\n      return;\n    }\n\n    for (MethodWrapper meth : node.getWrapper().getMethods()) {\n\n      if (meth.root != null) {\n\n        boolean replaced = false;\n\n        DirectGraph graph = meth.getOrBuildGraph();\n\n        HashSet<DirectNode> setVisited = new HashSet<>();\n        LinkedList<DirectNode> stack = new LinkedList<>();\n        stack.add(graph.first);\n\n        while (!stack.isEmpty()) {  // TODO: replace with interface iterator?\n\n          DirectNode nd = stack.removeFirst();\n\n          if (setVisited.contains(nd)) {\n            continue;\n          }\n          setVisited.add(nd);\n\n          for (int i = 0; i < nd.exprents.size(); i++) {\n            Exprent exprent = nd.exprents.get(i);\n\n            replaced |= replaceInvocations(node, meth, exprent);\n\n            if (exprent.type == Exprent.EXPRENT_INVOCATION) {\n              Exprent ret = replaceAccessExprent(node, meth, (InvocationExprent)exprent);\n\n              if (ret != null) {\n                nd.exprents.set(i, ret);\n                replaced = true;\n              }\n            }\n          }\n\n          stack.addAll(nd.successors);\n        }\n\n        if (replaced) {\n          computeMethodType(node, meth);\n        }\n      }\n    }\n\n    for (ClassNode child : node.nested) {\n      eliminateStaticAccess(child);\n    }\n  }\n\n\n  private boolean replaceInvocations(ClassNode caller, MethodWrapper meth, Exprent exprent) {\n\n    boolean res = false;\n\n    for (Exprent expr : exprent.getAllExprents()) {\n      res |= replaceInvocations(caller, meth, expr);\n    }\n\n    while (true) {\n\n      boolean found = false;\n\n      for (Exprent expr : exprent.getAllExprents()) {\n        if (expr.type == Exprent.EXPRENT_INVOCATION) {\n          Exprent newexpr = replaceAccessExprent(caller, meth, (InvocationExprent)expr);\n          if (newexpr != null) {\n            exprent.replaceExprent(expr, newexpr);\n            found = true;\n            res = true;\n            break;\n          }\n        }\n      }\n\n      if (!found) {\n        break;\n      }\n    }\n\n    return res;\n  }\n\n  private static boolean sameTree(ClassNode caller, ClassNode callee) {\n\n    if (caller.classStruct.qualifiedName.equals(callee.classStruct.qualifiedName)) {\n      return false;\n    }\n\n    while (caller.parent != null) {\n      caller = caller.parent;\n    }\n\n    while (callee.parent != null) {\n      callee = callee.parent;\n    }\n\n    return caller == callee;\n  }\n\n  private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {\n    ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(invexpr.getClassName());\n\n    MethodWrapper methsource = null;\n    if (node != null && node.getWrapper() != null) {\n      methsource = node.getWrapper().getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());\n    }\n\n    if (methsource == null || !mapMethodType.containsKey(methsource)) {\n      return null;\n    }\n\n    // if same method, return\n    if (node.classStruct.qualifiedName.equals(caller.classStruct.qualifiedName) &&\n        methsource.methodStruct.getName().equals(methdest.methodStruct.getName()) &&\n        methsource.methodStruct.getDescriptor().equals(methdest.methodStruct.getDescriptor())) {\n      // no recursive invocations permitted!\n      return null;\n    }\n\n    MethodAccess type = mapMethodType.get(methsource);\n\n    //\t\t// FIXME: impossible case. MethodAccess.NORMAL is not saved in the map\n    //\t\tif(type == MethodAccess.NORMAL) {\n    //\t\t\treturn null;\n    //\t\t}\n\n    if (!sameTree(caller, node)) {\n      return null;\n    }\n\n    DirectGraph graph = methsource.getOrBuildGraph();\n    Exprent source = graph.first.exprents.get(0);\n\n    Exprent retexprent = null;\n\n    switch (type) {\n      case FIELD_GET -> {\n        ExitExprent exsource = (ExitExprent)source;\n        if (exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this\n          VarExprent var = (VarExprent)exsource.getValue();\n          String varname = methsource.varproc.getVarName(new VarVersionPair(var));\n\n          if (!methdest.setOuterVarNames.contains(varname)) {\n            VarNamesCollector vnc = new VarNamesCollector();\n            vnc.addName(varname);\n\n            methdest.varproc.refreshVarNames(vnc);\n            methdest.setOuterVarNames.add(varname);\n          }\n\n          int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER);\n          VarExprent ret = new VarExprent(index, var.getVarType(), methdest.varproc);\n          methdest.varproc.setVarName(new VarVersionPair(index, 0), varname);\n\n          retexprent = ret;\n        }\n        else { // field\n          FieldExprent ret = (FieldExprent)exsource.getValue().copy();\n          if (!ret.isStatic()) {\n            ret.replaceExprent(ret.getInstance(), invexpr.getParameters().get(0));\n          }\n          retexprent = ret;\n        }\n      }\n      case FIELD_SET -> {\n        AssignmentExprent ret;\n        if (source.type == Exprent.EXPRENT_EXIT) {\n          ExitExprent extex = (ExitExprent)source;\n          ret = (AssignmentExprent)extex.getValue().copy();\n        }\n        else {\n          ret = (AssignmentExprent)source.copy();\n        }\n        FieldExprent fexpr = (FieldExprent)ret.getLeft();\n\n        if (fexpr.isStatic()) {\n          ret.replaceExprent(ret.getRight(), invexpr.getParameters().get(0));\n        }\n        else {\n          ret.replaceExprent(ret.getRight(), invexpr.getParameters().get(1));\n          fexpr.replaceExprent(fexpr.getInstance(), invexpr.getParameters().get(0));\n        }\n\n        // do not use copied bytecodes\n        ret.getLeft().bytecode = null;\n        ret.getRight().bytecode = null;\n\n        retexprent = ret;\n      }\n      case FUNCTION -> retexprent = replaceFunction(invexpr, source);\n      case METHOD -> {\n        if (source.type == Exprent.EXPRENT_EXIT) {\n          source = ((ExitExprent)source).getValue();\n        }\n\n        InvocationExprent invret = (InvocationExprent)source.copy();\n\n        int index = 0;\n        if (!invret.isStatic()) {\n          invret.replaceExprent(invret.getInstance(), invexpr.getParameters().get(0));\n          index = 1;\n        }\n\n        for (int i = 0; i < invret.getParameters().size(); i++) {\n          invret.replaceExprent(invret.getParameters().get(i), invexpr.getParameters().get(i + index));\n        }\n\n        retexprent = invret;\n      }\n    }\n\n\n    if (retexprent != null) {\n      // preserve original bytecodes\n      retexprent.bytecode = null;\n      retexprent.addBytecodeOffsets(invexpr.bytecode);\n\n      // hide synthetic access method\n      boolean hide = true;\n\n      if (node.type == ClassNode.CLASS_ROOT || (node.access & CodeConstants.ACC_STATIC) != 0) {\n        StructMethod mt = methsource.methodStruct;\n        if (!mt.isSynthetic()) {\n          hide = false;\n        }\n      }\n      if (hide) {\n        node.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));\n      }\n    }\n\n    return retexprent;\n  }\n\n  private static Exprent replaceFunction(final InvocationExprent invexpr, final Exprent source) {\n    FunctionExprent functionExprent = (FunctionExprent)((ExitExprent)source).getValue().copy();\n\n    List<Exprent> lstParameters = invexpr.getParameters();\n\n    FieldExprent fieldExprent = (FieldExprent)functionExprent.getLstOperands().get(0);\n    if (fieldExprent.isStatic()) {\n      if (!lstParameters.isEmpty()) {\n        return null;\n      }\n      return functionExprent;\n    }\n\n    if (lstParameters.size() != 1) {\n      return null;\n    }\n\n    fieldExprent.replaceExprent(fieldExprent.getInstance(), lstParameters.get(0));\n    return functionExprent;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.code;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.code.SimpleInstructionSequence;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;\nimport org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.FinallyProcessor;\n\nimport java.util.*;\n\npublic final class DeadCodeHelper {\n  public static void removeDeadBlocks(ControlFlowGraph graph) {\n    LinkedList<BasicBlock> stack = new LinkedList<>();\n    Set<BasicBlock> setStacked = new HashSet<>();\n\n    stack.add(graph.getFirst());\n    setStacked.add(graph.getFirst());\n\n    while (!stack.isEmpty()) {\n      BasicBlock block = stack.removeFirst();\n\n      List<BasicBlock> successors = new ArrayList<>(block.getSuccessors());\n      successors.addAll(block.getSuccessorExceptions());\n\n      for (BasicBlock successor : successors) {\n        if (!setStacked.contains(successor)) {\n          stack.add(successor);\n          setStacked.add(successor);\n        }\n      }\n    }\n\n    Set<BasicBlock> setAllBlocks = new HashSet<>(graph.getBlocks());\n    setAllBlocks.removeAll(setStacked);\n\n    for (BasicBlock block : setAllBlocks) {\n      graph.removeBlock(block);\n    }\n  }\n\n  public static void removeEmptyBlocks(ControlFlowGraph graph) {\n    List<BasicBlock> blocks = graph.getBlocks();\n\n    boolean cont;\n    do {\n      cont = false;\n\n      for (int i = blocks.size() - 1; i >= 0; i--) {\n        BasicBlock block = blocks.get(i);\n\n        if (removeEmptyBlock(graph, block, false)) {\n          cont = true;\n          break;\n        }\n      }\n    }\n    while (cont);\n  }\n\n  private static boolean removeEmptyBlock(ControlFlowGraph graph, BasicBlock block, boolean merging) {\n    boolean deletedRanges = false;\n\n    if (block.getSeq().isEmpty()) {\n      if (block.getSuccessors().size() > 1) {\n        if (block.getPredecessors().size() > 1) {\n          // ambiguous block\n          throw new RuntimeException(\"ERROR: empty block with multiple predecessors and successors found\");\n        }\n        else if (!merging) {\n          throw new RuntimeException(\"ERROR: empty block with multiple successors found\");\n        }\n      }\n\n      Set<BasicBlock> setExits = new HashSet<>(graph.getLast().getPredecessors());\n\n      if (block.getPredecessorExceptions().isEmpty() &&\n          (!setExits.contains(block) || block.getPredecessors().size() == 1)) {\n\n        if (setExits.contains(block)) {\n          BasicBlock predecessor = block.getPredecessors().get(0);\n\n          // FIXME: flag in the basic block\n          if (predecessor.getSuccessors().size() != 1 ||\n              (!predecessor.getSeq().isEmpty() &&\n               predecessor.getSeq().getLastInstr().group == CodeConstants.GROUP_SWITCH)) {\n            return false;\n          }\n        }\n\n        Set<BasicBlock> predecessors = new HashSet<>(block.getPredecessors());\n        Set<BasicBlock> successors = new HashSet<>(block.getSuccessors());\n\n        // collecting common exception ranges of predecessors and successors\n        Set<BasicBlock> setCommonExceptionHandlers = null;\n        for (int i = 0; i < 2; ++i) {\n          for (BasicBlock adjacent : i == 0 ? predecessors : successors) {\n            if (setCommonExceptionHandlers == null) {\n              setCommonExceptionHandlers = new HashSet<>(adjacent.getSuccessorExceptions());\n            }\n            else {\n              setCommonExceptionHandlers.retainAll(adjacent.getSuccessorExceptions());\n            }\n          }\n        }\n\n        // check the block to be in each of the common ranges\n        if (setCommonExceptionHandlers != null && !setCommonExceptionHandlers.isEmpty()) {\n          for (BasicBlock handler : setCommonExceptionHandlers) {\n            if (!block.getSuccessorExceptions().contains(handler)) {\n              return false;\n            }\n          }\n        }\n\n        // remove ranges consisting of this one block\n        List<ExceptionRangeCFG> lstRanges = graph.getExceptions();\n        for (int i = lstRanges.size() - 1; i >= 0; i--) {\n          ExceptionRangeCFG range = lstRanges.get(i);\n          List<BasicBlock> lst = range.getProtectedRange();\n\n          if (lst.size() == 1 && lst.get(0) == block) {\n            if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_EMPTY_RANGES)) {\n              block.removeSuccessorException(range.getHandler());\n              lstRanges.remove(i);\n\n              deletedRanges = true;\n            }\n            else {\n              return false;\n            }\n          }\n        }\n\n        // connect remaining nodes\n        if (merging) {\n          BasicBlock predecessor = block.getPredecessors().get(0);\n          predecessor.removeSuccessor(block);\n\n          List<BasicBlock> successorList = new ArrayList<>(block.getSuccessors());\n          for (BasicBlock successor : successorList) {\n            block.removeSuccessor(successor);\n            predecessor.addSuccessor(successor);\n          }\n        }\n        else {\n          for (BasicBlock predecessor : predecessors) {\n            for (BasicBlock successor : successors) {\n              predecessor.replaceSuccessor(block, successor);\n            }\n          }\n        }\n\n        // finally exit edges\n        Set<BasicBlock> setFinallyExits = graph.getFinallyExits();\n        if (setFinallyExits.contains(block)) {\n          setFinallyExits.remove(block);\n          setFinallyExits.add(predecessors.iterator().next());\n        }\n\n        // replace first if necessary\n        if (graph.getFirst() == block) {\n          if (successors.size() != 1) {\n            throw new RuntimeException(\"multiple or no entry blocks!\");\n          }\n          else {\n            graph.setFirst(successors.iterator().next());\n          }\n        }\n\n        // remove this block\n        graph.removeBlock(block);\n\n        if (deletedRanges) {\n          removeDeadBlocks(graph);\n        }\n      }\n    }\n\n    return deletedRanges;\n  }\n\n  public static boolean isDominator(ControlFlowGraph graph, BasicBlock block, BasicBlock dom) {\n    if (block == dom) {\n      return true;\n    }\n\n    Set<BasicBlock> marked = new HashSet<>();\n    List<BasicBlock> lstNodes = new LinkedList<>();\n    lstNodes.add(block);\n\n    while (!lstNodes.isEmpty()) {\n      BasicBlock node = lstNodes.remove(0);\n      if (marked.contains(node)) {\n        continue;\n      }\n      else {\n        marked.add(node);\n      }\n\n      if (node == graph.getFirst()) {\n        return false;\n      }\n\n      for (int i = 0; i < node.getPredecessors().size(); i++) {\n        BasicBlock predecessor = node.getPredecessors().get(i);\n        if (predecessor != dom && !marked.contains(predecessor)) {\n          lstNodes.add(predecessor);\n        }\n      }\n\n      for (int i = 0; i < node.getPredecessorExceptions().size(); i++) {\n        BasicBlock predecessor = node.getPredecessorExceptions().get(i);\n        if (predecessor != dom && !marked.contains(predecessor)) {\n          lstNodes.add(predecessor);\n        }\n      }\n    }\n\n    return true;\n  }\n\n  public static void removeGoTos(ControlFlowGraph graph) {\n    for (BasicBlock block : graph.getBlocks()) {\n      Instruction instr = block.getLastInstruction();\n\n      if (instr != null && instr.opcode == CodeConstants.opc_goto) {\n        block.getSeq().removeLast();\n      }\n    }\n\n    removeEmptyBlocks(graph);\n  }\n\n  public static void connectDummyExitBlock(ControlFlowGraph graph) {\n    BasicBlock exit = graph.getLast();\n    for (BasicBlock block : new HashSet<>(exit.getPredecessors())) {\n      exit.removePredecessor(block);\n      block.addSuccessor(exit);\n    }\n  }\n\n  public static void extendSynchronizedRangeToMonitorExit(ControlFlowGraph graph) {\n    while(true) {\n      boolean rangeExtended = false;\n\n      for (ExceptionRangeCFG range : graph.getExceptions()) {\n        Set<BasicBlock> predecessors = new HashSet<>();\n        for (BasicBlock block : range.getProtectedRange()) {\n          predecessors.addAll(block.getPredecessors());\n        }\n        for (BasicBlock basicBlock : range.getProtectedRange()) {\n          predecessors.remove(basicBlock);\n        }\n\n        if (predecessors.size() != 1) {\n          continue; // multiple predecessors -> obfuscated range\n        }\n\n        BasicBlock predecessor = predecessors.iterator().next();\n        InstructionSequence predecessorSeq = predecessor.getSeq();\n        if (predecessorSeq.isEmpty() || predecessorSeq.getLastInstr().opcode != CodeConstants.opc_monitorenter) {\n          continue; // not a synchronized range\n        }\n\n        boolean monitorExitInRange = false;\n        Set<BasicBlock> setProtectedBlocks = new HashSet<>(range.getProtectedRange());\n        setProtectedBlocks.add(range.getHandler());\n\n        for (BasicBlock block : setProtectedBlocks) {\n          InstructionSequence blockSeq = block.getSeq();\n          for (int i = 0; i < blockSeq.length(); i++) {\n            if (blockSeq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {\n              monitorExitInRange = true;\n              break;\n            }\n          }\n          if (monitorExitInRange) {\n            break;\n          }\n        }\n\n        if (monitorExitInRange) {\n          continue; // the protected range already contains MonitorExit\n        }\n\n        Set<BasicBlock> successors = new HashSet<>();\n        for (BasicBlock block : range.getProtectedRange()) {\n          successors.addAll(block.getSuccessors());\n        }\n        for (BasicBlock basicBlock : range.getProtectedRange()) {\n          successors.remove(basicBlock);\n        }\n\n        if (successors.size() != 1) {\n          continue; // non-unique successor\n        }\n\n        BasicBlock successor = successors.iterator().next();\n        InstructionSequence successorSeq = successor.getSeq();\n\n        int successorMonitorExitIndex = findMonitorExitIndex(successorSeq);\n        if (successorMonitorExitIndex < 0) {\n          continue; // no MonitorExit found in the single successor block\n        }\n\n        BasicBlock handlerBlock = range.getHandler();\n        if (handlerBlock.getSuccessors().size() != 1) {\n          continue; // non-unique handler successor\n        }\n        BasicBlock successorHandler = handlerBlock.getSuccessors().get(0);\n        InstructionSequence successorHandlerSeq = successorHandler.getSeq();\n        if (successorHandlerSeq.isEmpty() || successorHandlerSeq.getLastInstr().opcode != CodeConstants.opc_athrow) {\n          continue; // not a standard synchronized range\n        }\n\n        int handlerMonitorExitIndex = findMonitorExitIndex(successorHandlerSeq);\n        if (handlerMonitorExitIndex < 0) {\n          continue; // no MonitorExit found in the handler successor block\n        }\n\n        // checks successful, prerequisites satisfied, now extend the range\n        if (successorMonitorExitIndex < successorSeq.length() - 1) { // split block\n          SimpleInstructionSequence seq = new SimpleInstructionSequence();\n          for(int counter = 0; counter < successorMonitorExitIndex; counter++) {\n            seq.addInstruction(successorSeq.getInstr(0), -1);\n            successorSeq.removeInstruction(0);\n          }\n\n          // build a separate block\n          BasicBlock newBlock = new BasicBlock(++graph.last_id, seq);\n\n          // insert new block\n          for (BasicBlock block : successor.getPredecessors()) {\n            block.replaceSuccessor(successor, newBlock);\n          }\n\n          newBlock.addSuccessor(successor);\n          graph.getBlocks().addWithKey(newBlock, newBlock.id);\n\n          successor = newBlock;\n        }\n\n        // copy exception edges and extend protected ranges (successor block)\n        BasicBlock rangeExitBlock = successor.getPredecessors().get(0);\n        FinallyProcessor.copyExceptionEdges(graph, rangeExitBlock, successor);\n\n        // copy instructions (handler successor block)\n        InstructionSequence handlerSeq = handlerBlock.getSeq();\n        for(int counter = 0; counter < handlerMonitorExitIndex; counter++) {\n          handlerSeq.addInstruction(successorHandlerSeq.getInstr(0), -1);\n          successorHandlerSeq.removeInstruction(0);\n        }\n\n        rangeExtended = true;\n        break;\n      }\n\n      if (!rangeExtended) {\n        break;\n      }\n    }\n  }\n\n  private static int findMonitorExitIndex(InstructionSequence sequence) {\n    int index = -1;\n    for (int i = 0; i < sequence.length(); i++) {\n      if (sequence.getInstr(i).opcode == CodeConstants.opc_monitorexit) {\n        index = i;\n        break;\n      }\n    }\n    return index;\n  }\n\n  public static void incorporateValueReturns(ControlFlowGraph graph) {\n    for (BasicBlock block : graph.getBlocks()) {\n      InstructionSequence seq = block.getSeq();\n\n      int len = seq.length();\n      if (len > 0 && len < 3) {\n        boolean ok = false;\n\n        if (seq.getLastInstr().opcode >= CodeConstants.opc_ireturn && seq.getLastInstr().opcode <= CodeConstants.opc_return) {\n          if (len == 1) {\n            ok = true;\n          }\n          else if (seq.getLastInstr().opcode != CodeConstants.opc_return) {\n            ok = switch (seq.getInstr(0).opcode) {\n              case CodeConstants.opc_iload, CodeConstants.opc_lload, CodeConstants.opc_fload, CodeConstants.opc_dload,\n                CodeConstants.opc_aload, CodeConstants.opc_aconst_null, CodeConstants.opc_bipush, CodeConstants.opc_sipush,\n                CodeConstants.opc_lconst_0, CodeConstants.opc_lconst_1, CodeConstants.opc_fconst_0, CodeConstants.opc_fconst_1,\n                CodeConstants.opc_fconst_2, CodeConstants.opc_dconst_0, CodeConstants.opc_dconst_1, CodeConstants.opc_ldc,\n                CodeConstants.opc_ldc_w, CodeConstants.opc_ldc2_w -> true;\n              default -> false;\n            };\n          }\n        }\n\n        if (ok) {\n          if (!block.getPredecessors().isEmpty()) {\n            Set<BasicBlock> predecessorHandlersUnion = new HashSet<>();\n            Set<BasicBlock> predecessorHandlersIntersection = new HashSet<>();\n\n            boolean firstPredecessor = true;\n            for (BasicBlock predecessor : block.getPredecessors()) {\n              if (firstPredecessor) {\n                predecessorHandlersIntersection.addAll(predecessor.getSuccessorExceptions());\n                firstPredecessor = false;\n              }\n              else {\n                predecessorHandlersIntersection.retainAll(predecessor.getSuccessorExceptions());\n              }\n              predecessorHandlersUnion.addAll(predecessor.getSuccessorExceptions());\n            }\n\n            // add exception ranges from predecessors\n            for (BasicBlock basicBlock : block.getSuccessorExceptions()) {\n              predecessorHandlersIntersection.remove(basicBlock);\n            }\n\n            BasicBlock predecessor = block.getPredecessors().get(0);\n            for (BasicBlock handler : predecessorHandlersIntersection) {\n              ExceptionRangeCFG range = graph.getExceptionRange(handler, predecessor);\n              range.getProtectedRange().add(block);\n              block.addSuccessorException(handler);\n            }\n\n            // remove redundant ranges\n            Set<BasicBlock> setRangesToBeRemoved = new HashSet<>(block.getSuccessorExceptions());\n            setRangesToBeRemoved.removeAll(predecessorHandlersUnion);\n\n            for (BasicBlock handler : setRangesToBeRemoved) {\n              ExceptionRangeCFG range = graph.getExceptionRange(handler, block);\n\n              if (range.getProtectedRange().size() > 1) {\n                range.getProtectedRange().remove(block);\n                block.removeSuccessorException(handler);\n              }\n            }\n          }\n\n          if (block.getPredecessors().size() == 1 && block.getPredecessorExceptions().isEmpty()) {\n            BasicBlock predecessor = block.getPredecessors().get(0);\n            if (predecessor.getSuccessors().size() == 1) {\n              // add exception ranges of the predecessor\n              for (BasicBlock successor : predecessor.getSuccessorExceptions()) {\n                if (!block.getSuccessorExceptions().contains(successor)) {\n                  ExceptionRangeCFG range = graph.getExceptionRange(successor, predecessor);\n                  range.getProtectedRange().add(block);\n                  block.addSuccessorException(successor);\n                }\n              }\n\n              // remove superfluous ranges from successors\n              for (BasicBlock successor : new HashSet<>(block.getSuccessorExceptions())) {\n                if (!predecessor.getSuccessorExceptions().contains(successor)) {\n                  ExceptionRangeCFG range = graph.getExceptionRange(successor, block);\n                  if (range.getProtectedRange().size() > 1) {\n                    range.getProtectedRange().remove(block);\n                    block.removeSuccessorException(successor);\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  public static void mergeBasicBlocks(ControlFlowGraph graph) {\n    while (true) {\n      boolean merged = false;\n      int originBlocksCount = graph.getBlocks().size();\n\n      for (BasicBlock block : graph.getBlocks()) {\n        InstructionSequence seq = block.getSeq();\n\n        if (block.getSuccessors().size() == 1) {\n          BasicBlock next = block.getSuccessors().get(0);\n\n          if (next != graph.getLast() && (seq.isEmpty() || seq.getLastInstr().group != CodeConstants.GROUP_SWITCH)) {\n            if (next.getPredecessors().size() == 1 && next.getPredecessorExceptions().isEmpty() && next != graph.getFirst()) {\n              // TODO: implement a dummy start block\n              boolean sameRanges = true;\n              for (ExceptionRangeCFG range : graph.getExceptions()) {\n                if (range.getProtectedRange().contains(block) ^\n                    range.getProtectedRange().contains(next)) {\n                  sameRanges = false;\n                  break;\n                }\n              }\n\n              if (sameRanges) {\n                seq.addSequence(next.getSeq());\n                block.getOriginalOffsets().addAll(next.getOriginalOffsets());\n                next.getSeq().clear();\n\n                removeEmptyBlock(graph, next, true);\n\n                merged = true;\n                break;\n              }\n            }\n          }\n        }\n      }\n\n      if (!merged || graph.getBlocks().size() == originBlocksCount) {\n        break;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/ClasspathHelper.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\n\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic final class ClasspathHelper {\n\n  private static final Map<String, Method> METHOD_CACHE = Collections.synchronizedMap(new HashMap<>());\n\n  public static Method findMethod(String classname, String methodName, MethodDescriptor descriptor) {\n    String targetClass = classname.replace('/', '.');\n    String methodSignature = buildMethodSignature(targetClass + '.' + methodName, descriptor);\n\n    Method method;\n    if (METHOD_CACHE.containsKey(methodSignature)) {\n      method = METHOD_CACHE.get(methodSignature);\n    }\n    else {\n      method = findMethodOnClasspath(targetClass, methodSignature);\n      METHOD_CACHE.put(methodSignature, method);\n    }\n\n    return method;\n  }\n\n  private static Method findMethodOnClasspath(String targetClass, String methodSignature) {\n    try {\n      // use bootstrap classloader to only provide access to JRE classes\n      Class cls = new ClassLoader(null) {}.loadClass(targetClass);\n      for (Method mtd : cls.getMethods()) {\n        // use contains() to ignore access modifiers and thrown exceptions\n        if (mtd.toString().contains(methodSignature)) {\n          return mtd;\n        }\n      }\n    }\n    catch (Exception e) {\n      // ignore\n    }\n    return null;\n  }\n\n  private static String buildMethodSignature(String name, MethodDescriptor md) {\n    StringBuilder sb = new StringBuilder();\n\n    appendType(sb, md.ret);\n    sb.append(' ').append(name).append('(');\n    for (VarType param : md.params) {\n      appendType(sb, param);\n      sb.append(',');\n    }\n    if (sb.charAt(sb.length() - 1) == ',') {\n      sb.setLength(sb.length() - 1);\n    }\n    sb.append(')');\n\n    return sb.toString();\n  }\n\n  private static void appendType(StringBuilder sb, VarType type) {\n    sb.append(type.getValue().replace('/', '.'));\n    for (int i = 0; i < type.getArrayDim(); i++) {\n      sb.append(\"[]\");\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\n\nimport java.util.LinkedList;\n\n\npublic final class ClearStructHelper {\n\n  public static void clearStatements(RootStatement root) {\n\n    LinkedList<Statement> stack = new LinkedList<>();\n    stack.add(root);\n\n    while (!stack.isEmpty()) {\n\n      Statement stat = stack.removeFirst();\n\n      stat.clearTempInformation();\n\n      stack.addAll(stat.getStats());\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.ClassNameConstants;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.struct.consts.PooledConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\npublic final class ConcatenationHelper {\n\n  private static final String builderClass = \"java/lang/StringBuilder\";\n  private static final String bufferClass = \"java/lang/StringBuffer\";\n\n  private static final VarType builderType = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/StringBuilder\");\n  private static final VarType bufferType = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/StringBuffer\");\n\n\n  public static Exprent contractStringConcat(Exprent expr) {\n\n    Exprent exprTmp = null;\n    VarType cltype = null;\n\n    // first quick test\n    if (expr.type == Exprent.EXPRENT_INVOCATION) {\n      InvocationExprent iex = (InvocationExprent)expr;\n      if (\"toString\".equals(iex.getName())) {\n        if (builderClass.equals(iex.getClassName())) {\n          cltype = builderType;\n        }\n        else if (bufferClass.equals(iex.getClassName())) {\n          cltype = bufferType;\n        }\n        if (cltype != null) {\n          exprTmp = iex.getInstance();\n        }\n      }\n      else if (\"makeConcatWithConstants\".equals(iex.getName())) { // java 9 style\n        List<Exprent> parameters = extractParameters(iex.getBootstrapArguments(), iex);\n        if (parameters.size() >= 2) {\n          return createConcatExprent(parameters, expr.bytecode);\n        }\n      }\n    }\n\n    if (exprTmp == null) {\n      return expr;\n    }\n\n\n    // iterate in depth, collecting possible operands\n    List<Exprent> lstOperands = new ArrayList<>();\n\n    while (true) {\n\n      int found = 0;\n\n      switch (exprTmp.type) {\n        case Exprent.EXPRENT_INVOCATION -> {\n          InvocationExprent iex = (InvocationExprent)exprTmp;\n          if (isAppendConcat(iex, cltype)) {\n            lstOperands.add(0, iex.getParameters().get(0));\n            exprTmp = iex.getInstance();\n            found = 1;\n          }\n        }\n        case Exprent.EXPRENT_NEW -> {\n          NewExprent nex = (NewExprent)exprTmp;\n          if (isNewConcat(nex, cltype)) {\n            VarType[] params = nex.getConstructor().getDescriptor().params;\n            if (params.length == 1) {\n              lstOperands.add(0, nex.getConstructor().getParameters().get(0));\n            }\n            found = 2;\n          }\n        }\n      }\n\n      if (found == 0) {\n        return expr;\n      }\n      else if (found == 2) {\n        break;\n      }\n    }\n\n    int first2str = 0;\n    int index = 0;\n    while (index < lstOperands.size() && index < 2) {\n      if (lstOperands.get(index).getExprType().equals(VarType.VARTYPE_STRING)) {\n        first2str |= (index + 1);\n      }\n      index++;\n    }\n\n    if (first2str == 0) {\n      lstOperands.add(0, new ConstExprent(VarType.VARTYPE_STRING, \"\", expr.bytecode));\n    }\n\n    // remove redundant String.valueOf\n    for (int i = 0; i < lstOperands.size(); i++) {\n      Exprent rep = removeStringValueOf(lstOperands.get(i));\n\n      boolean ok = (i > 1);\n      if (!ok) {\n        boolean isstr = rep.getExprType().equals(VarType.VARTYPE_STRING);\n        ok = isstr || first2str != i + 1;\n\n        if (i == 0) {\n          first2str &= 2;\n        }\n      }\n\n      if (ok) {\n        lstOperands.set(i, rep);\n      }\n    }\n    return createConcatExprent(lstOperands, expr.bytecode);\n  }\n\n  private static Exprent createConcatExprent(List<Exprent> lstOperands, Set<Integer> bytecode) {\n    // build exprent to return\n    Exprent func = lstOperands.get(0);\n\n    for (int i = 1; i < lstOperands.size(); i++) {\n      func = new FunctionExprent(FunctionExprent.FUNCTION_STR_CONCAT, Arrays.asList(func, lstOperands.get(i)), bytecode);\n    }\n\n    return func;\n  }\n\n  // See StringConcatFactory in jdk sources\n  private static final char TAG_ARG = '\\u0001';\n  private static final char TAG_CONST = '\\u0002';\n\n  private static List<Exprent> extractParameters(List<PooledConstant> bootstrapArguments, InvocationExprent expr) {\n    List<Exprent> parameters = expr.getParameters();\n    if (bootstrapArguments != null) {\n      PooledConstant constant = bootstrapArguments.get(0);\n      if (constant.type == CodeConstants.CONSTANT_String) {\n        String recipe = ((PrimitiveConstant)constant).getString();\n\n        List<Exprent> res = new ArrayList<>();\n        StringBuilder acc = new StringBuilder();\n        int parameterId = 0;\n        int bootstrapArgumentId = 1;\n        for (int i = 0; i < recipe.length(); i++) {\n          char c = recipe.charAt(i);\n\n          if (c == TAG_CONST || c == TAG_ARG) {\n            // Detected a special tag, flush all accumulated characters\n            // as a constant first:\n            if (acc.length() > 0) {\n              res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode));\n              acc.setLength(0);\n            }\n\n            if (c == TAG_CONST) {\n              PooledConstant pooledConstant = bootstrapArguments.get(bootstrapArgumentId++);\n\n              if (pooledConstant.type == CodeConstants.CONSTANT_String) {\n                res.add(new ConstExprent(VarType.VARTYPE_STRING, ((PrimitiveConstant) pooledConstant).getString(), expr.bytecode));\n              }\n            }\n\n            if (c == TAG_ARG) {\n              Exprent exprent = parameters.get(parameterId++);\n\n              if ((exprent instanceof VarExprent varExprent) && res.isEmpty()) {\n\n                if (!VarType.VARTYPE_STRING.equals(varExprent.getVarType())) {\n                  // First item of concatenation is a variable and variable's type is not a String.\n                  // Prepend it with empty string literal to force resulting expression type to be String.\n                  res.add(new ConstExprent(VarType.VARTYPE_STRING, \"\", expr.bytecode));\n                }\n              }\n\n              res.add(exprent);\n            }\n          }\n          else {\n            // Not a special characters, this is a constant embedded into\n            // the recipe itself.\n            acc.append(c);\n          }\n        }\n\n        // Flush the remaining characters as constant:\n        if (acc.length() > 0) {\n          res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode));\n        }\n\n        if (res.size() == 1) {\n          //\n          // Only one element in result list.\n          // The most probable cause is an empty prefix/suffix.\n          // Something like this:\n          //\n          //   return \"\" + value;\n          //\n          // NOTE: Empty suffix is indistinguishable from empty prefix. We always assume latter.\n          //\n          res.add(0, new ConstExprent(VarType.VARTYPE_STRING, \"\", expr.bytecode));\n        }\n\n        return res;\n      }\n    }\n    return new ArrayList<>(parameters);\n  }\n\n  private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) {\n\n    if (\"append\".equals(expr.getName())) {\n      MethodDescriptor md = expr.getDescriptor();\n      if (md.ret.equals(cltype) && md.params.length == 1) {\n        VarType param = md.params[0];\n        switch (param.getType()) {\n          case CodeConstants.TYPE_OBJECT:\n            if (!param.equals(VarType.VARTYPE_STRING) &&\n                !param.equals(VarType.VARTYPE_OBJECT)) {\n              break;\n            }\n          case CodeConstants.TYPE_BOOLEAN:\n          case CodeConstants.TYPE_CHAR:\n          case CodeConstants.TYPE_DOUBLE:\n          case CodeConstants.TYPE_FLOAT:\n          case CodeConstants.TYPE_INT:\n          case CodeConstants.TYPE_LONG:\n            return true;\n          default:\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static boolean isNewConcat(NewExprent expr, VarType cltype) {\n    if (expr.getNewType().equals(cltype)) {\n      VarType[] params = expr.getConstructor().getDescriptor().params;\n      return params.length == 0 || params.length == 1 && params[0].equals(VarType.VARTYPE_STRING);\n    }\n\n    return false;\n  }\n\n  private static Exprent removeStringValueOf(Exprent exprent) {\n\n    if (exprent.type == Exprent.EXPRENT_INVOCATION) {\n      InvocationExprent iex = (InvocationExprent)exprent;\n      if (\"valueOf\".equals(iex.getName()) && ClassNameConstants.JAVA_LANG_STRING.equals(iex.getClassName())) {\n        MethodDescriptor md = iex.getDescriptor();\n        if (md.params.length == 1) {\n          VarType param = md.params[0];\n          switch (param.getType()) {\n            case CodeConstants.TYPE_OBJECT:\n              if (!param.equals(VarType.VARTYPE_OBJECT)) {\n                break;\n              }\n            case CodeConstants.TYPE_BOOLEAN:\n            case CodeConstants.TYPE_CHAR:\n            case CodeConstants.TYPE_DOUBLE:\n            case CodeConstants.TYPE_FLOAT:\n            case CodeConstants.TYPE_INT:\n            case CodeConstants.TYPE_LONG:\n              return iex.getParameters().get(0);\n          }\n        }\n      }\n    }\n\n    return exprent;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n\npublic final class DecHelper {\n\n  public static boolean checkStatementExceptions(List<? extends Statement> lst) {\n\n    Set<Statement> all = new HashSet<>(lst);\n\n    Set<Statement> handlers = new HashSet<>();\n    Set<Statement> intersection = null;\n\n    for (Statement stat : lst) {\n      Set<Statement> setNew = stat.getNeighboursSet(EdgeType.EXCEPTION, EdgeDirection.FORWARD);\n\n      if (intersection == null) {\n        intersection = setNew;\n      }\n      else {\n        HashSet<Statement> interclone = new HashSet<>(intersection);\n        interclone.removeAll(setNew);\n\n        intersection.retainAll(setNew);\n\n        setNew.removeAll(intersection);\n\n        handlers.addAll(interclone);\n        handlers.addAll(setNew);\n      }\n    }\n\n    for (Statement stat : handlers) {\n      if (!all.contains(stat) || !all.containsAll(stat.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.BACKWARD))) {\n        return false;\n      }\n    }\n\n    // check for other handlers (excluding head)\n    for (int i = 1; i < lst.size(); i++) {\n      Statement stat = lst.get(i);\n      if (!stat.getPredecessorEdges(EdgeType.EXCEPTION).isEmpty() && !handlers.contains(stat)) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  public static boolean isChoiceStatement(Statement head, List<? super Statement> lst) {\n\n    Statement post = null;\n\n    Set<Statement> setDest = head.getNeighboursSet(EdgeType.REGULAR, EdgeDirection.FORWARD);\n\n    if (setDest.contains(head)) {\n      return false;\n    }\n\n    while (true) {\n\n      lst.clear();\n\n      boolean repeat = false;\n\n      setDest.remove(post);\n\n      for (Statement stat : setDest) {\n        if (stat.getLastBasicType() != StatementType.GENERAL) {\n          if (post == null) {\n            post = stat;\n            repeat = true;\n            break;\n          }\n          else {\n            return false;\n          }\n        }\n\n        // preds\n        Set<Statement> setPred = stat.getNeighboursSet(EdgeType.REGULAR, EdgeDirection.BACKWARD);\n        setPred.remove(head);\n        if (setPred.contains(stat)) {\n          return false;\n        }\n\n        if (!setDest.containsAll(setPred) || setPred.size() > 1) {\n          if (post == null) {\n            post = stat;\n            repeat = true;\n            break;\n          }\n          else {\n            return false;\n          }\n        }\n        else if (setPred.size() == 1) {\n          Statement pred = setPred.iterator().next();\n          while (lst.contains(pred)) {\n            Set<Statement> setPredTemp = pred.getNeighboursSet(EdgeType.REGULAR, EdgeDirection.BACKWARD);\n            setPredTemp.remove(head);\n\n            if (!setPredTemp.isEmpty()) { // at most 1 predecessor\n              pred = setPredTemp.iterator().next();\n              if (pred == stat) {\n                return false;  // loop found\n              }\n            }\n            else {\n              break;\n            }\n          }\n        }\n\n        // succs\n        List<StatEdge> lstEdges = stat.getSuccessorEdges(EdgeType.DIRECT_ALL);\n        if (lstEdges.size() > 1) {\n          Set<Statement> setSucc = stat.getNeighboursSet(EdgeType.DIRECT_ALL, EdgeDirection.FORWARD);\n          setSucc.retainAll(setDest);\n\n          if (setSucc.size() > 0) {\n            return false;\n          }\n          else {\n            if (post == null) {\n              post = stat;\n              repeat = true;\n              break;\n            }\n            else {\n              return false;\n            }\n          }\n        }\n        else if (lstEdges.size() == 1) {\n\n          StatEdge edge = lstEdges.get(0);\n          if (edge.getType() == EdgeType.REGULAR) {\n            Statement statd = edge.getDestination();\n            if (head == statd) {\n              return false;\n            }\n            if (post != statd && !setDest.contains(statd)) {\n              if (post != null) {\n                return false;\n              }\n              else {\n                Set<Statement> set = statd.getNeighboursSet(EdgeType.REGULAR, EdgeDirection.BACKWARD);\n                if (set.size() > 1) {\n                  post = statd;\n                  repeat = true;\n                  break;\n                }\n                else {\n                  return false;\n                }\n              }\n            }\n          }\n        }\n\n        lst.add(stat);\n      }\n\n      if (!repeat) {\n        break;\n      }\n    }\n\n    lst.add(head);\n    lst.remove(post);\n\n    lst.add(0, post);\n\n    return true;\n  }\n\n  public static Set<Statement> getUniquePredExceptions(Statement head) {\n    Set<Statement> setHandlers = new HashSet<>(head.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.FORWARD));\n    setHandlers.removeIf(statement -> statement.getPredecessorEdges(EdgeType.EXCEPTION).size() > 1);\n    return setHandlers;\n  }\n\n  public static List<Exprent> copyExprentList(List<? extends Exprent> lst) {\n    List<Exprent> ret = new ArrayList<>();\n    for (Exprent expr : lst) {\n      ret.add(expr.copy());\n    }\n    return ret;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;\nimport org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.FastExtendedPostdominanceHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.deobfuscator.IrreducibleCFGDeobfuscator;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.util.FastFixedSetFactory;\nimport org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\n\npublic final class DomHelper {\n\n\n  private static RootStatement graphToStatement(ControlFlowGraph graph) {\n\n    VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<>();\n    VBStyleCollection<BasicBlock, Integer> blocks = graph.getBlocks();\n\n    for (BasicBlock block : blocks) {\n      stats.addWithKey(new BasicBlockStatement(block), block.id);\n    }\n\n    BasicBlock firstblock = graph.getFirst();\n    // head statement\n    Statement firstst = stats.getWithKey(firstblock.id);\n    // dummy exit statement\n    DummyExitStatement dummyexit = new DummyExitStatement();\n\n    Statement general;\n    if (stats.size() > 1 || firstblock.isSuccessor(firstblock)) { // multiple basic blocks or an infinite loop of one block\n      general = new GeneralStatement(firstst, stats, null);\n    }\n    else { // one straightforward basic block\n      RootStatement root = new RootStatement(firstst, dummyexit);\n      firstst.addSuccessor(new StatEdge(EdgeType.BREAK, firstst, dummyexit, root));\n\n      return root;\n    }\n\n    for (BasicBlock block : blocks) {\n      Statement stat = stats.getWithKey(block.id);\n\n      for (BasicBlock succ : block.getSuccessors()) {\n        Statement stsucc = stats.getWithKey(succ.id);\n\n        EdgeType type;\n        if (stsucc == firstst) {\n          type = EdgeType.CONTINUE;\n        }\n        else if (graph.getFinallyExits().contains(block)) {\n          type = EdgeType.FINALLY_EXIT;\n          stsucc = dummyexit;\n        }\n        else if (succ.id == graph.getLast().id) {\n          type = EdgeType.BREAK;\n          stsucc = dummyexit;\n        }\n        else {\n          type = EdgeType.REGULAR;\n        }\n\n        stat.addSuccessor(new StatEdge(type, stat, (type == EdgeType.CONTINUE) ? general : stsucc,\n                                       (type == EdgeType.REGULAR) ? null : general));\n      }\n\n      // exceptions edges\n      for (BasicBlock succex : block.getSuccessorExceptions()) {\n        Statement stsuccex = stats.getWithKey(succex.id);\n\n        ExceptionRangeCFG range = graph.getExceptionRange(succex, block);\n        if (!range.isCircular()) {\n          stat.addSuccessor(new StatEdge(stat, stsuccex, range.getExceptionTypes()));\n        }\n      }\n    }\n\n    general.buildContinueSet();\n    general.buildMonitorFlags();\n    return new RootStatement(general, dummyexit);\n  }\n\n  public static VBStyleCollection<List<Integer>, Integer> calcPostDominators(Statement container) {\n\n    HashMap<Statement, FastFixedSet<Statement>> lists = new HashMap<>();\n\n    StrongConnectivityHelper connectivityHelper = new StrongConnectivityHelper(container);\n\n    List<Statement> lstStats = container.getPostReversePostOrderList(connectivityHelper.getExitReps());\n\n    FastFixedSetFactory<Statement> factory = new FastFixedSetFactory<>(lstStats);\n\n    FastFixedSet<Statement> setFlagNodes = factory.spawnEmptySet();\n    setFlagNodes.setAllElements();\n\n    FastFixedSet<Statement> initSet = factory.spawnEmptySet();\n    initSet.setAllElements();\n\n    for (List<Statement> lst : connectivityHelper.getComponents()) {\n      FastFixedSet<Statement> tmpSet;\n\n      if (StrongConnectivityHelper.isExitComponent(lst)) {\n        tmpSet = factory.spawnEmptySet();\n        tmpSet.addAll(lst);\n      }\n      else {\n        tmpSet = initSet.getCopy();\n      }\n\n      for (Statement stat : lst) {\n        lists.put(stat, tmpSet);\n      }\n    }\n\n    do {\n\n      for (Statement stat : lstStats) {\n\n        if (!setFlagNodes.contains(stat)) {\n          continue;\n        }\n        setFlagNodes.remove(stat);\n\n        FastFixedSet<Statement> doms = lists.get(stat);\n        FastFixedSet<Statement> domsSuccs = factory.spawnEmptySet();\n\n        List<Statement> lstSuccs = stat.getNeighbours(EdgeType.REGULAR, EdgeDirection.FORWARD);\n        for (int j = 0; j < lstSuccs.size(); j++) {\n          Statement succ = lstSuccs.get(j);\n          FastFixedSet<Statement> succlst = lists.get(succ);\n\n          if (j == 0) {\n            domsSuccs.union(succlst);\n          }\n          else {\n            domsSuccs.intersection(succlst);\n          }\n        }\n\n        if (!domsSuccs.contains(stat)) {\n          domsSuccs.add(stat);\n        }\n\n        if (!Objects.equals(domsSuccs, doms)) {\n\n          lists.put(stat, domsSuccs);\n\n          List<Statement> lstPreds = stat.getNeighbours(EdgeType.REGULAR, EdgeDirection.BACKWARD);\n          for (Statement pred : lstPreds) {\n            setFlagNodes.add(pred);\n          }\n        }\n      }\n    }\n    while (!setFlagNodes.isEmpty());\n\n    VBStyleCollection<List<Integer>, Integer> ret = new VBStyleCollection<>();\n    List<Statement> lstRevPost = container.getReversePostOrderList(); // sort order crucial!\n\n    final HashMap<Integer, Integer> mapSortOrder = new HashMap<>();\n    for (int i = 0; i < lstRevPost.size(); i++) {\n      mapSortOrder.put(lstRevPost.get(i).id, i);\n    }\n\n    for (Statement st : lstStats) {\n\n      List<Integer> lstPosts = new ArrayList<>();\n      for (Statement stt : lists.get(st)) {\n        lstPosts.add(stt.id);\n      }\n\n      lstPosts.sort(Comparator.comparing(mapSortOrder::get));\n\n      if (lstPosts.size() > 1 && lstPosts.get(0).intValue() == st.id) {\n        lstPosts.add(lstPosts.remove(0));\n      }\n\n      ret.addWithKey(lstPosts, st.id);\n    }\n\n    return ret;\n  }\n\n  public static RootStatement parseGraph(ControlFlowGraph graph) {\n\n    RootStatement root = graphToStatement(graph);\n\n    if (!processStatement(root, new HashMap<>())) {\n\n      //\t\t\ttry {\n      //\t\t\t\tDotExporter.toDotFile(root.getFirst().getStats().get(13), new File(\"c:\\\\Temp\\\\stat1.dot\"));\n      //\t\t\t} catch (Exception ex) {\n      //\t\t\t\tex.printStackTrace();\n      //\t\t\t}\n      throw new RuntimeException(\"parsing failure!\");\n    }\n\n    LabelHelper.lowContinueLabels(root, new HashSet<>());\n\n    SequenceHelper.condenseSequences(root);\n    root.buildMonitorFlags();\n\n    // build synchronized statements\n    buildSynchronized(root);\n\n    return root;\n  }\n\n  public static void removeSynchronizedHandler(Statement stat) {\n\n    for (Statement st : stat.getStats()) {\n      removeSynchronizedHandler(st);\n    }\n\n    if (stat.type == StatementType.SYNCHRONIZED) {\n      ((SynchronizedStatement)stat).removeExc();\n    }\n  }\n\n\n  private static void buildSynchronized(Statement stat) {\n\n    for (Statement st : stat.getStats()) {\n      buildSynchronized(st);\n    }\n\n    if (stat.type == StatementType.SEQUENCE) {\n\n      while (true) {\n\n        boolean found = false;\n\n        List<Statement> lst = stat.getStats();\n        for (int i = 0; i < lst.size() - 1; i++) {\n          Statement current = lst.get(i);  // basic block\n\n          if (current.isMonitorEnter()) {\n\n            Statement next = lst.get(i + 1);\n            Statement nextDirect = next;\n\n            while (next.type == StatementType.SEQUENCE) {\n              next = next.getFirst();\n            }\n\n            if (next.type == StatementType.CATCH_ALL) {\n\n              CatchAllStatement ca = (CatchAllStatement)next;\n\n              if (ca.getFirst().isContainsMonitorExit() && ca.getHandler().isContainsMonitorExit()) {\n\n                // remove the head block from sequence\n                current.removeSuccessor(current.getSuccessorEdges(EdgeType.DIRECT_ALL).get(0));\n\n                for (StatEdge edge : current.getPredecessorEdges(EdgeType.DIRECT_ALL)) {\n                  current.removePredecessor(edge);\n                  edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, nextDirect);\n                  nextDirect.addPredecessor(edge);\n                }\n\n                stat.getStats().removeWithKey(current.id);\n                stat.setFirst(stat.getStats().get(0));\n\n                // new statement\n                SynchronizedStatement sync = new SynchronizedStatement(current, ca.getFirst(), ca.getHandler());\n                sync.setAllParent();\n\n                for (StatEdge edge : new HashSet<>(ca.getLabelEdges())) {\n                  sync.addLabeledEdge(edge);\n                }\n\n                current.addSuccessor(new StatEdge(EdgeType.REGULAR, current, ca.getFirst()));\n\n                ca.getParent().replaceStatement(ca, sync);\n                found = true;\n                break;\n              }\n            }\n          }\n        }\n\n        if (!found) {\n          break;\n        }\n      }\n    }\n  }\n\n  private static boolean processStatement(Statement general, HashMap<Integer, Set<Integer>> mapExtPost) {\n\n    if (general.type == StatementType.ROOT) {\n      Statement stat = general.getFirst();\n      if (stat.type != StatementType.GENERAL) {\n        return true;\n      }\n      else {\n        boolean complete = processStatement(stat, mapExtPost);\n        if (complete) {\n          // replace general purpose statement with simple one\n          general.replaceStatement(stat, stat.getFirst());\n        }\n        return complete;\n      }\n    }\n\n    boolean mapRefreshed = mapExtPost.isEmpty();\n\n    for (int mapstage = 0; mapstage < 2; mapstage++) {\n\n      for (int reducibility = 0;\n           reducibility < 5;\n           reducibility++) { // FIXME: implement proper node splitting. For now up to 5 nodes in sequence are splitted.\n\n        if (reducibility > 0) {\n\n          // take care of irreducible control flow graphs\n          if (IrreducibleCFGDeobfuscator.isStatementIrreducible(general)) {\n            if (!IrreducibleCFGDeobfuscator.splitIrreducibleNode(general)) {\n              DecompilerContext.getLogger().writeMessage(\"Irreducible statement cannot be decomposed!\", IFernflowerLogger.Severity.ERROR);\n              break;\n            }\n          }\n          else {\n            if (mapRefreshed) { // last chance lost\n              DecompilerContext.getLogger().writeMessage(\"Statement cannot be decomposed although reducible!\", IFernflowerLogger.Severity.ERROR);\n            }\n            break;\n          }\n          mapExtPost = new HashMap<>();\n          mapRefreshed = true;\n        }\n\n        for (int i = 0; i < 2; i++) {\n\n          boolean forceall = i != 0;\n\n          while (true) {\n\n            if (findSimpleStatements(general, mapExtPost)) {\n              reducibility = 0;\n            }\n\n            if (general.type == StatementType.PLACEHOLDER) {\n              return true;\n            }\n\n            Statement stat = findGeneralStatement(general, forceall, mapExtPost);\n\n            if (stat != null) {\n              boolean complete = processStatement(stat, general.getFirst() == stat ? mapExtPost : new HashMap<>());\n\n              if (complete) {\n                // replace general purpose statement with simple one\n                general.replaceStatement(stat, stat.getFirst());\n              }\n              else {\n                return false;\n              }\n\n              mapExtPost = new HashMap<>();\n              mapRefreshed = true;\n              reducibility = 0;\n            }\n            else {\n              break;\n            }\n          }\n        }\n      }\n\n      if (mapRefreshed) {\n        break;\n      }\n      else {\n        mapExtPost = new HashMap<>();\n      }\n    }\n\n    return false;\n  }\n\n  private static Statement findGeneralStatement(Statement stat, boolean forceall, HashMap<Integer, Set<Integer>> mapExtPost) {\n\n    VBStyleCollection<Statement, Integer> stats = stat.getStats();\n    VBStyleCollection<List<Integer>, Integer> vbPost;\n\n    if (mapExtPost.isEmpty()) {\n      FastExtendedPostdominanceHelper extpost = new FastExtendedPostdominanceHelper();\n      mapExtPost.putAll(extpost.getExtendedPostdominators(stat));\n    }\n\n    if (forceall) {\n      vbPost = new VBStyleCollection<>();\n      List<Statement> lstAll = stat.getPostReversePostOrderList();\n\n      for (Statement st : lstAll) {\n        Set<Integer> set = mapExtPost.get(st.id);\n        if (set != null) {\n          vbPost.addWithKey(new ArrayList<>(set), st.id); // FIXME: sort order!!\n        }\n      }\n\n      // tail statements\n      Set<Integer> setFirst = mapExtPost.get(stat.getFirst().id);\n      if (setFirst != null) {\n        for (Integer id : setFirst) {\n          List<Integer> lst = vbPost.getWithKey(id);\n          if (lst == null) {\n            vbPost.addWithKey(lst = new ArrayList<>(), id);\n          }\n          lst.add(id);\n        }\n      }\n    }\n    else {\n      vbPost = calcPostDominators(stat);\n    }\n\n    for (int k = 0; k < vbPost.size(); k++) {\n\n      Integer headid = vbPost.getKey(k);\n      List<Integer> posts = vbPost.get(k);\n\n      if (!mapExtPost.containsKey(headid) &&\n          !(posts.size() == 1 && posts.get(0).equals(headid))) {\n        continue;\n      }\n\n      Statement head = stats.getWithKey(headid);\n\n      Set<Integer> setExtPosts = mapExtPost.get(headid);\n\n      for (Integer postId : posts) {\n        if (!postId.equals(headid) && !setExtPosts.contains(postId)) {\n          continue;\n        }\n\n        Statement post = stats.getWithKey(postId);\n\n        if (post == null) { // possible in case of an inherited postdominance set\n          continue;\n        }\n\n        boolean same = (post == head);\n\n        HashSet<Statement> setNodes = new HashSet<>();\n        HashSet<Statement> setPreds = new HashSet<>();\n\n        // collect statement nodes\n        HashSet<Statement> setHandlers = new HashSet<>();\n        setHandlers.add(head);\n        while (true) {\n\n          boolean hdfound = false;\n          for (Statement handler : setHandlers) {\n            if (setNodes.contains(handler)) {\n              continue;\n            }\n\n            boolean addhd = (setNodes.size() == 0); // first handler == head\n            if (!addhd) {\n              List<Statement> hdsupp = handler.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.BACKWARD);\n              addhd = (setNodes.containsAll(hdsupp) && (setNodes.size() > hdsupp.size()\n                                                        || setNodes.size() == 1)); // strict subset\n            }\n\n            if (addhd) {\n              LinkedList<Statement> lstStack = new LinkedList<>();\n              lstStack.add(handler);\n\n              while (!lstStack.isEmpty()) {\n                Statement st = lstStack.remove(0);\n\n                if (!(setNodes.contains(st) || (!same && st == post))) {\n                  setNodes.add(st);\n                  if (st != head) {\n                    // record predeccessors except for the head\n                    setPreds.addAll(st.getNeighbours(EdgeType.REGULAR, EdgeDirection.BACKWARD));\n                  }\n\n                  // put successors on the stack\n                  lstStack.addAll(st.getNeighbours(EdgeType.REGULAR, EdgeDirection.FORWARD));\n\n                  // exception edges\n                  setHandlers.addAll(st.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.FORWARD));\n                }\n              }\n\n              hdfound = true;\n              setHandlers.remove(handler);\n              break;\n            }\n          }\n\n          if (!hdfound) {\n            break;\n          }\n        }\n\n        // check exception handlers\n        setHandlers.clear();\n        for (Statement st : setNodes) {\n          setHandlers.addAll(st.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.FORWARD));\n        }\n        setHandlers.removeAll(setNodes);\n\n        boolean excok = true;\n        for (Statement handler : setHandlers) {\n          if (!handler.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.BACKWARD).containsAll(setNodes)) {\n            excok = false;\n            break;\n          }\n        }\n\n        // build statement and return\n        if (excok) {\n          Statement res;\n\n          setPreds.removeAll(setNodes);\n          if (setPreds.size() == 0) {\n            if ((setNodes.size() > 1 ||\n                 head.getNeighbours(EdgeType.REGULAR, EdgeDirection.BACKWARD).contains(head))\n                && setNodes.size() < stats.size()) {\n              if (checkSynchronizedCompleteness(setNodes)) {\n                res = new GeneralStatement(head, setNodes, same ? null : post);\n                stat.collapseNodesToStatement(res);\n\n                return res;\n              }\n            }\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n\n  private static boolean checkSynchronizedCompleteness(Set<Statement> setNodes) {\n    // check exit nodes\n    for (Statement stat : setNodes) {\n      if (stat.isMonitorEnter()) {\n        List<StatEdge> lstSuccs = stat.getSuccessorEdges(EdgeType.DIRECT_ALL);\n        if (lstSuccs.size() != 1 || lstSuccs.get(0).getType() != EdgeType.REGULAR) {\n          return false;\n        }\n\n        if (!setNodes.contains(lstSuccs.get(0).getDestination())) {\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n\n  private static boolean findSimpleStatements(Statement stat, HashMap<Integer, Set<Integer>> mapExtPost) {\n\n    boolean found, success = false;\n\n    do {\n      found = false;\n\n      List<Statement> lstStats = stat.getPostReversePostOrderList();\n      for (Statement st : lstStats) {\n\n        Statement result = detectStatement(st);\n\n        if (result != null) {\n\n          if (stat.type == StatementType.GENERAL && result.getFirst() == stat.getFirst() &&\n              stat.getStats().size() == result.getStats().size()) {\n            // mark general statement\n            stat.type = StatementType.PLACEHOLDER;\n          }\n\n          stat.collapseNodesToStatement(result);\n\n          // update the postdominator map\n          if (!mapExtPost.isEmpty()) {\n            HashSet<Integer> setOldNodes = new HashSet<>();\n            for (Statement old : result.getStats()) {\n              setOldNodes.add(old.id);\n            }\n\n            Integer newid = result.id;\n\n            for (Integer key : new ArrayList<>(mapExtPost.keySet())) {\n              Set<Integer> set = mapExtPost.get(key);\n\n              int oldsize = set.size();\n              set.removeAll(setOldNodes);\n\n              if (setOldNodes.contains(key)) {\n                mapExtPost.computeIfAbsent(newid, k -> new HashSet<>()).addAll(set);\n                mapExtPost.remove(key);\n              }\n              else {\n                if (set.size() < oldsize) {\n                  set.add(newid);\n                }\n              }\n            }\n          }\n\n\n          found = true;\n          break;\n        }\n      }\n\n      if (found) {\n        success = true;\n      }\n    }\n    while (found);\n\n    return success;\n  }\n\n\n  private static Statement detectStatement(Statement head) {\n\n    Statement res;\n\n    if ((res = DoStatement.isHead(head)) != null) {\n      return res;\n    }\n\n    if ((res = SwitchStatement.isHead(head)) != null) {\n      return res;\n    }\n\n    if ((res = IfStatement.isHead(head)) != null) {\n      return res;\n    }\n\n    // synchronized statements will be identified later\n    // right now they are recognized as catchall\n\n    if ((res = SequenceStatement.isHead2Block(head)) != null) {\n      return res;\n    }\n\n    if ((res = CatchStatement.isHead(head)) != null) {\n      return res;\n    }\n\n    if ((res = CatchAllStatement.isHead(head)) != null) {\n      return res;\n    }\n\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\npublic final class ExitHelper {\n  public static boolean condenseExits(RootStatement root) {\n    int changed = integrateExits(root);\n\n    if (changed > 0) {\n      cleanUpUnreachableBlocks(root);\n      SequenceHelper.condenseSequences(root);\n    }\n\n    return (changed > 0);\n  }\n\n  private static void cleanUpUnreachableBlocks(Statement stat) {\n    boolean found;\n    do {\n      found = false;\n\n      for (int i = 0; i < stat.getStats().size(); i++) {\n        Statement st = stat.getStats().get(i);\n\n        cleanUpUnreachableBlocks(st);\n\n        if (st.type == StatementType.SEQUENCE && st.getStats().size() > 1) {\n\n          Statement last = st.getStats().getLast();\n          Statement secondlast = st.getStats().get(st.getStats().size() - 2);\n\n          if (last.getExprents() == null || !last.getExprents().isEmpty()) {\n            if (!secondlast.hasBasicSuccEdge()) {\n\n              Set<Statement> set = last.getNeighboursSet(EdgeType.DIRECT_ALL, EdgeDirection.BACKWARD);\n              set.remove(secondlast);\n\n              if (set.isEmpty()) {\n                last.setExprents(new ArrayList<>());\n                found = true;\n                break;\n              }\n            }\n          }\n        }\n      }\n    }\n    while (found);\n  }\n\n  private static int integrateExits(Statement stat) {\n    int ret = 0;\n    Statement dest;\n\n    if (stat.getExprents() == null) {\n      while (true) {\n        int changed = 0;\n\n        for (Statement st : stat.getStats()) {\n          changed = integrateExits(st);\n          if (changed > 0) {\n            ret = 1;\n            break;\n          }\n        }\n\n        if (changed == 0) {\n          break;\n        }\n      }\n\n      if (stat.type == StatementType.IF) {\n        IfStatement ifst = (IfStatement)stat;\n        if (ifst.getIfstat() == null) {\n          StatEdge ifedge = ifst.getIfEdge();\n          dest = isExitEdge(ifedge);\n          if (dest != null) {\n            BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(\n              DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));\n            bstat.setExprents(DecHelper.copyExprentList(dest.getExprents()));\n\n            ifst.getFirst().removeSuccessor(ifedge);\n            StatEdge newedge = new StatEdge(EdgeType.REGULAR, ifst.getFirst(), bstat);\n            ifst.getFirst().addSuccessor(newedge);\n            ifst.setIfEdge(newedge);\n            ifst.setIfstat(bstat);\n            ifst.getStats().addWithKey(bstat, bstat.id);\n            bstat.setParent(ifst);\n\n            StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0);\n            StatEdge newexitedge = new StatEdge(EdgeType.BREAK, bstat, oldexitedge.getDestination());\n            bstat.addSuccessor(newexitedge);\n            oldexitedge.closure.addLabeledEdge(newexitedge);\n            ret = 1;\n          }\n        }\n      }\n    }\n\n\n    if (stat.getAllSuccessorEdges().size() == 1 &&\n        stat.getAllSuccessorEdges().get(0).getType() == EdgeType.BREAK &&\n        stat.getLabelEdges().isEmpty()) {\n      Statement parent = stat.getParent();\n      if (stat != parent.getFirst() || (parent.type != StatementType.IF &&\n                                        parent.type != StatementType.SWITCH)) {\n\n        StatEdge destedge = stat.getAllSuccessorEdges().get(0);\n        dest = isExitEdge(destedge);\n        if (dest != null) {\n          stat.removeSuccessor(destedge);\n\n          BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(\n            DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));\n          bstat.setExprents(DecHelper.copyExprentList(dest.getExprents()));\n\n          StatEdge oldexitedge = dest.getAllSuccessorEdges().get(0);\n          StatEdge newexitedge = new StatEdge(EdgeType.BREAK, bstat, oldexitedge.getDestination());\n          bstat.addSuccessor(newexitedge);\n          oldexitedge.closure.addLabeledEdge(newexitedge);\n\n          SequenceStatement block = new SequenceStatement(Arrays.asList(stat, bstat));\n          block.setAllParent();\n\n          parent.replaceStatement(stat, block);\n          // LabelHelper.lowContinueLabels not applicable because of forward continue edges\n          // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>());\n          // do it by hand\n          for (StatEdge prededge : block.getPredecessorEdges(EdgeType.CONTINUE)) {\n            block.removePredecessor(prededge);\n            prededge.getSource().changeEdgeNode(EdgeDirection.FORWARD, prededge, stat);\n            stat.addPredecessor(prededge);\n            stat.addLabeledEdge(prededge);\n          }\n\n          stat.addSuccessor(new StatEdge(EdgeType.REGULAR, stat, bstat));\n\n          for (StatEdge edge : dest.getAllPredecessorEdges()) {\n            if (!edge.explicit && stat.containsStatementStrict(edge.getSource()) &&\n                MergeHelper.isDirectPath(edge.getSource().getParent(), bstat)) {\n\n              dest.removePredecessor(edge);\n              edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, bstat);\n              bstat.addPredecessor(edge);\n\n              if (!stat.containsStatementStrict(edge.closure)) {\n                stat.addLabeledEdge(edge);\n              }\n            }\n          }\n\n          ret = 2;\n        }\n      }\n    }\n\n    return ret;\n  }\n\n  private static Statement isExitEdge(StatEdge edge) {\n    Statement dest = edge.getDestination();\n\n    if (edge.getType() == EdgeType.BREAK && dest.type == StatementType.BASIC_BLOCK && edge.explicit && (edge.labeled || isOnlyEdge(edge))) {\n      List<Exprent> data = dest.getExprents();\n\n      if (data != null && data.size() == 1) {\n        if (data.get(0).type == Exprent.EXPRENT_EXIT) {\n          return dest;\n        }\n      }\n    }\n\n    return null;\n  }\n\n  private static boolean isOnlyEdge(StatEdge edge) {\n    Statement stat = edge.getDestination();\n\n    for (StatEdge ed : stat.getAllPredecessorEdges()) {\n      if (ed != edge) {\n        if (ed.getType() == EdgeType.REGULAR) {\n          Statement source = ed.getSource();\n\n          if (source.type == StatementType.BASIC_BLOCK || (source.type == StatementType.IF &&\n                                                           ((IfStatement)source).iftype == IfStatement.IFTYPE_IF) ||\n              (source.type == StatementType.DO && ((DoStatement)source).getLoopType() != LoopType.DO)) {\n            return false;\n          }\n        }\n        else {\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n\n  public static void removeRedundantReturns(RootStatement root) {\n    DummyExitStatement dummyExit = root.getDummyExit();\n\n    for (StatEdge edge : dummyExit.getAllPredecessorEdges()) {\n      if (!edge.explicit) {\n        Statement source = edge.getSource();\n        List<Exprent> lstExpr = source.getExprents();\n        if (lstExpr != null && !lstExpr.isEmpty()) {\n          Exprent expr = lstExpr.get(lstExpr.size() - 1);\n          if (expr.type == Exprent.EXPRENT_EXIT) {\n            ExitExprent ex = (ExitExprent)expr;\n            if (ex.getExitType() == ExitExprent.EXIT_RETURN && ex.getValue() == null) {\n              // remove redundant return\n              dummyExit.addBytecodeOffsets(ex.bytecode);\n              lstExpr.remove(lstExpr.size() - 1);\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TypeAnnotationWriteHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructTypePathEntry;\nimport org.jetbrains.java.decompiler.struct.attr.StructBootstrapMethodsAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.consts.LinkConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PooledConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.Type;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class ExprProcessor {\n  @SuppressWarnings(\"SpellCheckingInspection\")\n  public static final String UNDEFINED_TYPE_STRING = \"<undefinedtype>\";\n  public static final String UNKNOWN_TYPE_STRING = \"<unknown>\";\n  public static final String NULL_TYPE_STRING = \"<null>\";\n\n  private static final Map<Integer, Integer> functionMap = Map.of(\n    CodeConstants.opc_arraylength, FunctionExprent.FUNCTION_ARRAY_LENGTH,\n    CodeConstants.opc_checkcast, FunctionExprent.FUNCTION_CAST,\n    CodeConstants.opc_instanceof, FunctionExprent.FUNCTION_INSTANCEOF\n  );\n\n  private static final VarType[] constants = {\n    VarType.VARTYPE_INT, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_CLASS, VarType.VARTYPE_STRING\n  };\n\n  private static final VarType[] varTypes = {\n    VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT\n  };\n\n  private static final VarType[] arrTypes = {\n    VarType.VARTYPE_INT, VarType.VARTYPE_LONG, VarType.VARTYPE_FLOAT, VarType.VARTYPE_DOUBLE, VarType.VARTYPE_OBJECT,\n    VarType.VARTYPE_BOOLEAN, VarType.VARTYPE_CHAR, VarType.VARTYPE_SHORT\n  };\n\n  private static final int[] func1 = {\n    FunctionExprent.FUNCTION_ADD, FunctionExprent.FUNCTION_SUB, FunctionExprent.FUNCTION_MUL, FunctionExprent.FUNCTION_DIV,\n    FunctionExprent.FUNCTION_REM\n  };\n  private static final int[] func2 = {\n    FunctionExprent.FUNCTION_SHL, FunctionExprent.FUNCTION_SHR, FunctionExprent.FUNCTION_USHR, FunctionExprent.FUNCTION_AND,\n    FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR\n  };\n  private static final int[] func3 = {\n    FunctionExprent.FUNCTION_I2L, FunctionExprent.FUNCTION_I2F, FunctionExprent.FUNCTION_I2D, FunctionExprent.FUNCTION_L2I,\n    FunctionExprent.FUNCTION_L2F, FunctionExprent.FUNCTION_L2D, FunctionExprent.FUNCTION_F2I, FunctionExprent.FUNCTION_F2L,\n    FunctionExprent.FUNCTION_F2D, FunctionExprent.FUNCTION_D2I, FunctionExprent.FUNCTION_D2L, FunctionExprent.FUNCTION_D2F,\n    FunctionExprent.FUNCTION_I2B, FunctionExprent.FUNCTION_I2C, FunctionExprent.FUNCTION_I2S\n  };\n  private static final int[] func4 = {\n    FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL,\n    FunctionExprent.FUNCTION_DCMPG\n  };\n  private static final int[] func5 = {\n    IfExprent.IF_EQ, IfExprent.IF_NE, IfExprent.IF_LT, IfExprent.IF_GE, IfExprent.IF_GT, IfExprent.IF_LE\n  };\n  private static final int[] func6 = {\n    IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPGT, IfExprent.IF_ICMPLE,\n    IfExprent.IF_ACMPEQ, IfExprent.IF_ACMPNE\n  };\n  private static final int[] func7 = {IfExprent.IF_NULL, IfExprent.IF_NONNULL};\n  private static final int[] func8 = {MonitorExprent.MONITOR_ENTER, MonitorExprent.MONITOR_EXIT};\n\n  private static final int[] arrTypeIds = {\n    CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_DOUBLE,\n    CodeConstants.TYPE_BYTE, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT, CodeConstants.TYPE_LONG\n  };\n\n  private static final int[] negIfs = {\n    IfExprent.IF_NE, IfExprent.IF_EQ, IfExprent.IF_GE, IfExprent.IF_LT, IfExprent.IF_LE, IfExprent.IF_GT, IfExprent.IF_NONNULL,\n    IfExprent.IF_NULL, IfExprent.IF_ICMPNE, IfExprent.IF_ICMPEQ, IfExprent.IF_ICMPGE, IfExprent.IF_ICMPLT, IfExprent.IF_ICMPLE,\n    IfExprent.IF_ICMPGT, IfExprent.IF_ACMPNE, IfExprent.IF_ACMPEQ\n  };\n\n  private static final String[] typeNames = {\"byte\", \"char\", \"double\", \"float\", \"int\", \"long\", \"short\", \"boolean\"};\n\n  private static final String EMPTY_ENTRY_POINTS_KEY = \"-\";\n\n  private final MethodDescriptor methodDescriptor;\n  private final VarProcessor varProcessor;\n\n  public ExprProcessor(MethodDescriptor md, VarProcessor varProc) {\n    methodDescriptor = md;\n    varProcessor = varProc;\n  }\n\n  public void processStatement(RootStatement root, StructClass cl) {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n    cancellationManager.checkCanceled();\n    FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper();\n    DirectGraph dgraph = flattenHelper.buildDirectGraph(root);\n\n    // collect `finally` entry points\n    Set<String> setFinallyShortRangeEntryPoints = new HashSet<>();\n    for (List<FinallyPathWrapper> wrappers : dgraph.mapShortRangeFinallyPaths.values()) {\n      for (FinallyPathWrapper wrapper : wrappers) {\n        setFinallyShortRangeEntryPoints.add(wrapper.entry);\n      }\n    }\n\n    Set<String> setFinallyLongRangeEntryPaths = new HashSet<>();\n    for (List<FinallyPathWrapper> wrappers : dgraph.mapLongRangeFinallyPaths.values()) {\n      for (FinallyPathWrapper wrapper : wrappers) {\n        setFinallyLongRangeEntryPaths.add(wrapper.source + \"##\" + wrapper.entry);\n      }\n    }\n\n    Map<String, VarExprent> mapCatch = new HashMap<>();\n    collectCatchVars(root, flattenHelper, mapCatch);\n\n    Map<DirectNode, Map<String, PrimitiveExpressionList>> mapData = new HashMap<>();\n\n    LinkedList<DirectNode> stack = new LinkedList<>();\n    LinkedList<LinkedList<String>> stackEntryPoint = new LinkedList<>();\n\n    stack.add(dgraph.first);\n    stackEntryPoint.add(new LinkedList<>());\n\n    Map<String, PrimitiveExpressionList> map = new HashMap<>();\n    map.put(EMPTY_ENTRY_POINTS_KEY, new PrimitiveExpressionList());\n    mapData.put(dgraph.first, map);\n\n    while (!stack.isEmpty()) {\n      cancellationManager.checkCanceled();\n      DirectNode node = stack.removeFirst();\n      LinkedList<String> entryPoints = stackEntryPoint.removeFirst();\n\n      PrimitiveExpressionList data;\n      if (mapCatch.containsKey(node.id)) {\n        data = getExpressionData(mapCatch.get(node.id));\n      }\n      else {\n        data = mapData.get(node).get(buildEntryPointKey(entryPoints));\n      }\n\n      BasicBlockStatement block = node.block;\n      if (block != null) {\n        processBlock(block, data, cl);\n        block.setExprents(data.getExpressions());\n      }\n\n      String currentEntrypoint = entryPoints.isEmpty() ? null : entryPoints.getLast();\n\n      for (DirectNode nd : node.successors) {\n        cancellationManager.checkCanceled();\n        boolean isSuccessor = true;\n\n        if (currentEntrypoint != null && dgraph.mapLongRangeFinallyPaths.containsKey(node.id)) {\n          isSuccessor = false;\n          for (FinallyPathWrapper wrapper : dgraph.mapLongRangeFinallyPaths.get(node.id)) {\n            if (wrapper.source.equals(currentEntrypoint) && wrapper.destination.equals(nd.id)) {\n              isSuccessor = true;\n              break;\n            }\n          }\n        }\n\n        if (isSuccessor) {\n          Map<String, PrimitiveExpressionList> successorMap = mapData.computeIfAbsent(nd, k -> new HashMap<>());\n          LinkedList<String> nodeEntryPoints = new LinkedList<>(entryPoints);\n\n          if (setFinallyLongRangeEntryPaths.contains(node.id + \"##\" + nd.id)) {\n            nodeEntryPoints.addLast(node.id);\n          }\n          else if (!setFinallyShortRangeEntryPoints.contains(nd.id) && dgraph.mapLongRangeFinallyPaths.containsKey(node.id)) {\n            nodeEntryPoints.removeLast(); // `currentEntryPoint` should not be null at this point\n          }\n\n          // handling of entry point loops\n          int successorEntryIndex = nodeEntryPoints.indexOf(nd.id);\n          if (successorEntryIndex >= 0) {\n            // we are in a loop (e.g., continue in a 'finally' block): drop all entry points in the list beginning with successor index\n            for (int elementsToRemove = nodeEntryPoints.size() - successorEntryIndex; elementsToRemove > 0; elementsToRemove--) {\n              nodeEntryPoints.removeLast();\n            }\n          }\n\n          String nodeEntryKey = buildEntryPointKey(nodeEntryPoints);\n          if (!successorMap.containsKey(nodeEntryKey)) {\n            successorMap.put(nodeEntryKey, data.copy());\n            stack.add(nd);\n            stackEntryPoint.add(nodeEntryPoints);\n          }\n        }\n      }\n    }\n\n    initStatementExprEntries(root);\n  }\n\n  // FIXME: Ugly code, to be rewritten. A tuple class is needed.\n  private static String buildEntryPointKey(LinkedList<String> entryPoints) {\n    if (entryPoints.isEmpty()) {\n      return EMPTY_ENTRY_POINTS_KEY;\n    }\n    else if (entryPoints.size() == 1) {\n      return entryPoints.getFirst();\n    }\n    else {\n      return String.join(\":\", entryPoints);\n    }\n  }\n\n  private static void collectCatchVars(Statement stat, FlattenStatementsHelper flattenHelper, Map<String, VarExprent> map) {\n    List<VarExprent> lst = null;\n\n    if (stat.type == StatementType.CATCH_ALL) {\n      CatchAllStatement catchall = (CatchAllStatement)stat;\n      if (!catchall.isFinally()) {\n        lst = catchall.getVars();\n      }\n    }\n    else if (stat.type == StatementType.TRY_CATCH) {\n      lst = ((CatchStatement)stat).getVars();\n    }\n\n    if (lst != null) {\n      for (int i = 1; i < stat.getStats().size(); i++) {\n        map.put(flattenHelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0], lst.get(i - 1));\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      collectCatchVars(st, flattenHelper, map);\n    }\n  }\n\n  private static void initStatementExprEntries(Statement stat) {\n    stat.initExprents();\n\n    for (Statement st : stat.getStats()) {\n      initStatementExprEntries(st);\n    }\n  }\n\n  public void processBlock(BasicBlockStatement stat, PrimitiveExpressionList data, StructClass cl) {\n    ConstantPool pool = cl.getPool();\n    StructBootstrapMethodsAttribute bootstrap = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_BOOTSTRAP_METHODS);\n    BasicBlock block = stat.getBlock();\n    ExpressionStack stack = data.getStack();\n    List<Exprent> exprList = data.getExpressions();\n    InstructionSequence seq = block.getSeq();\n\n    for (int i = 0; i < seq.length(); i++) {\n      Instruction instr = seq.getInstr(i);\n      Integer offset = block.getOriginalOffset(i);\n      Set<Integer> offsets = offset >= 0 ? Set.of(offset) : null;\n\n      switch (instr.opcode) {\n        case CodeConstants.opc_aconst_null:\n          pushEx(stack, exprList, new ConstExprent(VarType.VARTYPE_NULL, null, offsets));\n          break;\n        case CodeConstants.opc_bipush:\n        case CodeConstants.opc_sipush:\n          pushEx(stack, exprList, new ConstExprent(instr.operand(0), true, offsets));\n          break;\n        case CodeConstants.opc_lconst_0:\n        case CodeConstants.opc_lconst_1:\n          pushEx(stack, exprList, new ConstExprent(VarType.VARTYPE_LONG, (long)(instr.opcode - CodeConstants.opc_lconst_0), offsets));\n          break;\n        case CodeConstants.opc_fconst_0:\n        case CodeConstants.opc_fconst_1:\n        case CodeConstants.opc_fconst_2:\n          pushEx(stack, exprList, new ConstExprent(VarType.VARTYPE_FLOAT, (float)(instr.opcode - CodeConstants.opc_fconst_0), offsets));\n          break;\n        case CodeConstants.opc_dconst_0:\n        case CodeConstants.opc_dconst_1:\n          pushEx(stack, exprList, new ConstExprent(VarType.VARTYPE_DOUBLE, (double)(instr.opcode - CodeConstants.opc_dconst_0), offsets));\n          break;\n        case CodeConstants.opc_ldc:\n        case CodeConstants.opc_ldc_w:\n        case CodeConstants.opc_ldc2_w: {\n          PooledConstant cn = pool.getConstant(instr.operand(0));\n          if (cn instanceof PrimitiveConstant) {\n            pushEx(stack, exprList, new ConstExprent(constants[cn.type - CodeConstants.CONSTANT_Integer], ((PrimitiveConstant)cn).value, offsets));\n          }\n          else if (cn instanceof LinkConstant) {\n            //TODO: for now treat Links as Strings\n            pushEx(stack, exprList, new ConstExprent(VarType.VARTYPE_STRING, ((LinkConstant)cn).elementName, offsets));\n          }\n          break;\n        }\n        case CodeConstants.opc_iload:\n        case CodeConstants.opc_lload:\n        case CodeConstants.opc_fload:\n        case CodeConstants.opc_dload:\n        case CodeConstants.opc_aload:\n          pushEx(stack, exprList, new VarExprent(instr.operand(0), varTypes[instr.opcode - CodeConstants.opc_iload], varProcessor, offset));\n          break;\n        case CodeConstants.opc_iaload:\n        case CodeConstants.opc_laload:\n        case CodeConstants.opc_faload:\n        case CodeConstants.opc_daload:\n        case CodeConstants.opc_aaload:\n        case CodeConstants.opc_baload:\n        case CodeConstants.opc_caload:\n        case CodeConstants.opc_saload: {\n          Exprent index = stack.pop();\n          Exprent arr = stack.pop();\n          VarType type = instr.opcode == CodeConstants.opc_laload ? VarType.VARTYPE_LONG : instr.opcode == CodeConstants.opc_daload ? VarType.VARTYPE_DOUBLE : null;\n          pushEx(stack, exprList, new ArrayExprent(arr, index, arrTypes[instr.opcode - CodeConstants.opc_iaload], offsets), type);\n          break;\n        }\n        case CodeConstants.opc_istore:\n        case CodeConstants.opc_lstore:\n        case CodeConstants.opc_fstore:\n        case CodeConstants.opc_dstore:\n        case CodeConstants.opc_astore: {\n          Exprent value = stack.pop();\n          int varIndex = instr.operand(0);\n          VarExprent left = new VarExprent(varIndex, varTypes[instr.opcode - CodeConstants.opc_istore], varProcessor, nextMeaningfulOffset(block, i));\n          exprList.add(new AssignmentExprent(left, value, offsets));\n          break;\n        }\n        case CodeConstants.opc_iastore:\n        case CodeConstants.opc_lastore:\n        case CodeConstants.opc_fastore:\n        case CodeConstants.opc_dastore:\n        case CodeConstants.opc_aastore:\n        case CodeConstants.opc_bastore:\n        case CodeConstants.opc_castore:\n        case CodeConstants.opc_sastore: {\n          Exprent value = stack.pop();\n          Exprent index = stack.pop();\n          Exprent array = stack.pop();\n          ArrayExprent left = new ArrayExprent(array, index, arrTypes[instr.opcode - CodeConstants.opc_iastore], offsets);\n          exprList.add(new AssignmentExprent(left, value, offsets));\n          break;\n        }\n        case CodeConstants.opc_iadd:\n        case CodeConstants.opc_ladd:\n        case CodeConstants.opc_fadd:\n        case CodeConstants.opc_dadd:\n        case CodeConstants.opc_isub:\n        case CodeConstants.opc_lsub:\n        case CodeConstants.opc_fsub:\n        case CodeConstants.opc_dsub:\n        case CodeConstants.opc_imul:\n        case CodeConstants.opc_lmul:\n        case CodeConstants.opc_fmul:\n        case CodeConstants.opc_dmul:\n        case CodeConstants.opc_idiv:\n        case CodeConstants.opc_ldiv:\n        case CodeConstants.opc_fdiv:\n        case CodeConstants.opc_ddiv:\n        case CodeConstants.opc_irem:\n        case CodeConstants.opc_lrem:\n        case CodeConstants.opc_frem:\n        case CodeConstants.opc_drem:\n          pushEx(stack, exprList, new FunctionExprent(func1[(instr.opcode - CodeConstants.opc_iadd) / 4], stack, offsets));\n          break;\n        case CodeConstants.opc_ishl:\n        case CodeConstants.opc_lshl:\n        case CodeConstants.opc_ishr:\n        case CodeConstants.opc_lshr:\n        case CodeConstants.opc_iushr:\n        case CodeConstants.opc_lushr:\n        case CodeConstants.opc_iand:\n        case CodeConstants.opc_land:\n        case CodeConstants.opc_ior:\n        case CodeConstants.opc_lor:\n        case CodeConstants.opc_ixor:\n        case CodeConstants.opc_lxor:\n          pushEx(stack, exprList, new FunctionExprent(func2[(instr.opcode - CodeConstants.opc_ishl) / 2], stack, offsets));\n          break;\n        case CodeConstants.opc_ineg:\n        case CodeConstants.opc_lneg:\n        case CodeConstants.opc_fneg:\n        case CodeConstants.opc_dneg:\n          pushEx(stack, exprList, new FunctionExprent(FunctionExprent.FUNCTION_NEG, stack, offsets));\n          break;\n        case CodeConstants.opc_iinc: {\n          VarExprent varExpr = new VarExprent(instr.operand(0), VarType.VARTYPE_INT, varProcessor);\n          int type = instr.operand(1) < 0 ? FunctionExprent.FUNCTION_SUB : FunctionExprent.FUNCTION_ADD;\n          List<Exprent> operands = Arrays.asList(varExpr.copy(), new ConstExprent(VarType.VARTYPE_INT, Math.abs(instr.operand(1)), null));\n          exprList.add(new AssignmentExprent(varExpr, new FunctionExprent(type, operands, offsets), offsets));\n          break;\n        }\n        case CodeConstants.opc_i2l:\n        case CodeConstants.opc_i2f:\n        case CodeConstants.opc_i2d:\n        case CodeConstants.opc_l2i:\n        case CodeConstants.opc_l2f:\n        case CodeConstants.opc_l2d:\n        case CodeConstants.opc_f2i:\n        case CodeConstants.opc_f2l:\n        case CodeConstants.opc_f2d:\n        case CodeConstants.opc_d2i:\n        case CodeConstants.opc_d2l:\n        case CodeConstants.opc_d2f:\n        case CodeConstants.opc_i2b:\n        case CodeConstants.opc_i2c:\n        case CodeConstants.opc_i2s:\n          pushEx(stack, exprList, new FunctionExprent(func3[instr.opcode - CodeConstants.opc_i2l], stack, offsets));\n          break;\n        case CodeConstants.opc_lcmp:\n        case CodeConstants.opc_fcmpl:\n        case CodeConstants.opc_fcmpg:\n        case CodeConstants.opc_dcmpl:\n        case CodeConstants.opc_dcmpg:\n          pushEx(stack, exprList, new FunctionExprent(func4[instr.opcode - CodeConstants.opc_lcmp], stack, offsets));\n          break;\n        case CodeConstants.opc_ifeq:\n        case CodeConstants.opc_ifne:\n        case CodeConstants.opc_iflt:\n        case CodeConstants.opc_ifge:\n        case CodeConstants.opc_ifgt:\n        case CodeConstants.opc_ifle:\n          exprList.add(new IfExprent(negIfs[func5[instr.opcode - CodeConstants.opc_ifeq]], stack, offsets));\n          break;\n        case CodeConstants.opc_if_icmpeq:\n        case CodeConstants.opc_if_icmpne:\n        case CodeConstants.opc_if_icmplt:\n        case CodeConstants.opc_if_icmpge:\n        case CodeConstants.opc_if_icmpgt:\n        case CodeConstants.opc_if_icmple:\n        case CodeConstants.opc_if_acmpeq:\n        case CodeConstants.opc_if_acmpne:\n          exprList.add(new IfExprent(negIfs[func6[instr.opcode - CodeConstants.opc_if_icmpeq]], stack, offsets));\n          break;\n        case CodeConstants.opc_ifnull:\n        case CodeConstants.opc_ifnonnull:\n          exprList.add(new IfExprent(negIfs[func7[instr.opcode - CodeConstants.opc_ifnull]], stack, offsets));\n          break;\n        case CodeConstants.opc_tableswitch:\n        case CodeConstants.opc_lookupswitch:\n          exprList.add(new SwitchExprent(stack.pop(), offsets));\n          break;\n        case CodeConstants.opc_ireturn:\n        case CodeConstants.opc_lreturn:\n        case CodeConstants.opc_freturn:\n        case CodeConstants.opc_dreturn:\n        case CodeConstants.opc_areturn:\n        case CodeConstants.opc_return:\n        case CodeConstants.opc_athrow:\n          exprList.add(new ExitExprent(instr.opcode == CodeConstants.opc_athrow ? ExitExprent.EXIT_THROW : ExitExprent.EXIT_RETURN,\n                                       instr.opcode == CodeConstants.opc_return ? null : stack.pop(),\n                                       instr.opcode == CodeConstants.opc_athrow ? null : methodDescriptor.ret,\n                                       offsets));\n          break;\n        case CodeConstants.opc_monitorenter:\n        case CodeConstants.opc_monitorexit:\n          exprList.add(new MonitorExprent(func8[instr.opcode - CodeConstants.opc_monitorenter], stack.pop(), offsets));\n          break;\n        case CodeConstants.opc_checkcast:\n        case CodeConstants.opc_instanceof:\n          stack.push(new ConstExprent(new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true), null, null));\n        case CodeConstants.opc_arraylength:\n          pushEx(stack, exprList, new FunctionExprent(functionMap.get(instr.opcode), stack, offsets));\n          break;\n        case CodeConstants.opc_getstatic:\n        case CodeConstants.opc_getfield:\n          pushEx(stack, exprList,\n                 new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == CodeConstants.opc_getstatic ? null : stack.pop(), offsets));\n          break;\n        case CodeConstants.opc_putstatic:\n        case CodeConstants.opc_putfield:\n          Exprent valField = stack.pop();\n          Exprent exprField =\n            new FieldExprent(pool.getLinkConstant(instr.operand(0)), instr.opcode == CodeConstants.opc_putstatic ? null : stack.pop(), offsets);\n          exprList.add(new AssignmentExprent(exprField, valField, offsets));\n          break;\n        case CodeConstants.opc_invokevirtual:\n        case CodeConstants.opc_invokespecial:\n        case CodeConstants.opc_invokestatic:\n        case CodeConstants.opc_invokeinterface:\n        case CodeConstants.opc_invokedynamic:\n          if (instr.opcode != CodeConstants.opc_invokedynamic || instr.bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) {\n            LinkConstant invoke_constant = pool.getLinkConstant(instr.operand(0));\n\n            List<PooledConstant> bootstrap_arguments = null;\n            if (instr.opcode == CodeConstants.opc_invokedynamic && bootstrap != null) {\n              bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1);\n            }\n\n            InvocationExprent exprInv = new InvocationExprent(instr.opcode, invoke_constant, bootstrap_arguments, stack, offsets);\n            if (exprInv.getDescriptor().ret.getType() == CodeConstants.TYPE_VOID) {\n              exprList.add(exprInv);\n            }\n            else {\n              pushEx(stack, exprList, exprInv);\n            }\n          }\n          break;\n        case CodeConstants.opc_new:\n        case CodeConstants.opc_anewarray:\n        case CodeConstants.opc_multianewarray:\n          int dimensions = (instr.opcode == CodeConstants.opc_new) ? 0 : (instr.opcode == CodeConstants.opc_anewarray) ? 1 : instr.operand(1);\n          VarType arrType = new VarType(pool.getPrimitiveConstant(instr.operand(0)).getString(), true);\n          if (instr.opcode != CodeConstants.opc_multianewarray) {\n            arrType = arrType.resizeArrayDim(arrType.getArrayDim() + dimensions);\n          }\n          pushEx(stack, exprList, new NewExprent(arrType, stack, dimensions, offsets));\n          break;\n        case CodeConstants.opc_newarray:\n          pushEx(stack, exprList, new NewExprent(new VarType(arrTypeIds[instr.operand(0) - 4], 1), stack, 1, offsets));\n          break;\n        case CodeConstants.opc_dup:\n          pushEx(stack, exprList, stack.getByOffset(-1).copy());\n          break;\n        case CodeConstants.opc_dup_x1:\n          insertByOffsetEx(-2, stack, exprList, -1);\n          break;\n        case CodeConstants.opc_dup_x2:\n          if (stack.getByOffset(-2).getExprType().getStackSize() == 2) {\n            insertByOffsetEx(-2, stack, exprList, -1);\n          }\n          else {\n            insertByOffsetEx(-3, stack, exprList, -1);\n          }\n          break;\n        case CodeConstants.opc_dup2:\n          if (stack.getByOffset(-1).getExprType().getStackSize() == 2) {\n            pushEx(stack, exprList, stack.getByOffset(-1).copy());\n          }\n          else {\n            pushEx(stack, exprList, stack.getByOffset(-2).copy());\n            pushEx(stack, exprList, stack.getByOffset(-2).copy());\n          }\n          break;\n        case CodeConstants.opc_dup2_x1:\n          if (stack.getByOffset(-1).getExprType().getStackSize() == 2) {\n            insertByOffsetEx(-2, stack, exprList, -1);\n          }\n          else {\n            insertByOffsetEx(-3, stack, exprList, -2);\n            insertByOffsetEx(-3, stack, exprList, -1);\n          }\n          break;\n        case CodeConstants.opc_dup2_x2:\n          if (stack.getByOffset(-1).getExprType().getStackSize() == 2) {\n            if (stack.getByOffset(-2).getExprType().getStackSize() == 2) {\n              insertByOffsetEx(-2, stack, exprList, -1);\n            }\n            else {\n              insertByOffsetEx(-3, stack, exprList, -1);\n            }\n          }\n          else {\n            if (stack.getByOffset(-3).getExprType().getStackSize() == 2) {\n              insertByOffsetEx(-3, stack, exprList, -2);\n              insertByOffsetEx(-3, stack, exprList, -1);\n            }\n            else {\n              insertByOffsetEx(-4, stack, exprList, -2);\n              insertByOffsetEx(-4, stack, exprList, -1);\n            }\n          }\n          break;\n        case CodeConstants.opc_swap:\n          insertByOffsetEx(-2, stack, exprList, -1);\n          stack.pop();\n          break;\n        case CodeConstants.opc_pop:\n          stack.pop();\n          break;\n        case CodeConstants.opc_pop2:\n          if (stack.getByOffset(-1).getExprType().getStackSize() == 1) {\n            // Since the value at the top of the stack is a value of category 1 (JVMS9 2.11.1),\n            // we should remove one more item from the stack.\n            // See JVMS9 pop2 chapter.\n            stack.pop();\n          }\n          stack.pop();\n          break;\n      }\n    }\n  }\n\n  private static int nextMeaningfulOffset(BasicBlock block, int index) {\n    InstructionSequence seq = block.getSeq();\n    while (++index < seq.length()) {\n      switch (seq.getInstr(index).opcode) {\n        case CodeConstants.opc_nop, CodeConstants.opc_istore, CodeConstants.opc_lstore, CodeConstants.opc_fstore, CodeConstants.opc_dstore, CodeConstants.opc_astore -> {\n          continue;\n        }\n      }\n      return block.getOriginalOffset(index);\n    }\n\n    List<BasicBlock> successors = block.getSuccessors();\n    if (successors.size() == 1) {\n      return successors.get(0).getOriginalOffset(0);\n    }\n\n    return -1;\n  }\n\n  private void pushEx(ExpressionStack stack, List<Exprent> exprlist, Exprent exprent) {\n    pushEx(stack, exprlist, exprent, null);\n  }\n\n  private void pushEx(ExpressionStack stack, List<Exprent> exprlist, Exprent exprent, VarType vartype) {\n    int varIndex = VarExprent.STACK_BASE + stack.size();\n    VarExprent var = new VarExprent(varIndex, vartype == null ? exprent.getExprType() : vartype, varProcessor);\n    var.setStack(true);\n\n    exprlist.add(new AssignmentExprent(var, exprent, null));\n    stack.push(var.copy());\n  }\n\n  private void insertByOffsetEx(int offset, ExpressionStack stack, List<Exprent> exprlist, int copyOffset) {\n\n    int base = VarExprent.STACK_BASE + stack.size();\n\n    LinkedList<VarExprent> lst = new LinkedList<>();\n\n    for (int i = -1; i >= offset; i--) {\n      Exprent varEx = stack.pop();\n      VarExprent varNew = new VarExprent(base + i + 1, varEx.getExprType(), varProcessor);\n      varNew.setStack(true);\n      exprlist.add(new AssignmentExprent(varNew, varEx, null));\n      lst.add(0, (VarExprent)varNew.copy());\n    }\n\n    Exprent exprent = lst.get(lst.size() + copyOffset).copy();\n    VarExprent var = new VarExprent(base + offset, exprent.getExprType(), varProcessor);\n    var.setStack(true);\n    exprlist.add(new AssignmentExprent(var, exprent, null));\n    lst.add(0, (VarExprent)var.copy());\n\n    for (VarExprent expr : lst) {\n      stack.push(expr);\n    }\n  }\n\n  public static String getTypeName(VarType type, List<TypeAnnotationWriteHelper> typePathWriteHelper) {\n    return getTypeName(type, true, typePathWriteHelper);\n  }\n\n  public static String getTypeName(VarType type, boolean getShort, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    int tp = type.getType();\n    StringBuilder sb = new StringBuilder();\n    typeAnnWriteHelpers = writeTypeAnnotationBeforeType(type, sb, typeAnnWriteHelpers);\n    if (tp <= CodeConstants.TYPE_BOOLEAN) {\n      sb.append(typeNames[tp]);\n      return sb.toString();\n    }\n    else if (tp == CodeConstants.TYPE_UNKNOWN) {\n      sb.append(UNKNOWN_TYPE_STRING);\n      return sb.toString(); // INFO: should not occur\n    }\n    else if (tp == CodeConstants.TYPE_NULL) {\n      sb.append(NULL_TYPE_STRING);\n      return sb.toString(); // INFO: should not occur\n    }\n    else if (tp == CodeConstants.TYPE_VOID) {\n      sb.append(\"void\");\n      return sb.toString();\n    }\n    else if (tp == CodeConstants.TYPE_OBJECT) {\n      String ret;\n      if (getShort) {\n        ret = DecompilerContext.getImportCollector().getNestedName(type.getValue());\n      } else {\n        ret = buildJavaClassName(type.getValue());\n      }\n      if (ret == null) {\n        return UNDEFINED_TYPE_STRING; // FIXME: a warning should be logged\n      }\n      List<String> nestedTypes = Arrays.asList(ret.split(\"\\\\.\"));\n      writeNestedClass(sb, type, nestedTypes, typeAnnWriteHelpers);\n      popNestedTypeAnnotation(typeAnnWriteHelpers);\n      return sb.toString();\n    }\n\n    throw new RuntimeException(\"invalid type\");\n  }\n\n  public static List<TypeAnnotationWriteHelper> writeTypeAnnotationBeforeType(\n    Type type,\n    StringBuilder sb,\n    List<TypeAnnotationWriteHelper> typeAnnWriteHelpers\n  ) {\n    return typeAnnWriteHelpers.stream().filter(typeAnnWriteHelper -> { // TODO remove duplicate\n      if (typeAnnWriteHelper.getAnnotation().isWrittenBeforeType(type)) {\n        typeAnnWriteHelper.writeTo(sb);\n        return false;\n      }\n      StructTypePathEntry pathEntry = typeAnnWriteHelper.getPaths().peek();\n      if (pathEntry != null\n          && pathEntry.getTypePathEntryKind() == StructTypePathEntry.Kind.NESTED.getId()\n          && type.isAnnotatable()\n      ) typeAnnWriteHelper.getPaths().pop();\n      return true;\n    }).collect(Collectors.toList());\n  }\n\n  public static List<TypeAnnotationWriteHelper> writeNestedClass(\n    StringBuilder sb,\n    Type type,\n    List<String> nestedTypes,\n    List<TypeAnnotationWriteHelper> typeAnnWriteHelpers\n  ) {\n    List<ClassesProcessor.ClassNode> enclosingClasses = enclosingClassList();\n    StringBuilder curPathBuilder = new StringBuilder(type.getValue().substring(0, type.getValue().lastIndexOf('/') + 1));\n    for (int i = 0; i < nestedTypes.size(); i++) {\n      String nestedType = nestedTypes.get(i);\n      boolean shouldWrite = true;\n      if (!enclosingClasses.isEmpty() && i != nestedTypes.size() - 1) {\n        String enclosingType = enclosingClasses.remove(0).simpleName;\n        shouldWrite = !nestedType.equals(enclosingType);\n      }\n      if (i == 0) { // the first annotation can be written already\n        if (!sb.toString().isEmpty()) shouldWrite = true; // write if annotation exists\n      } else {\n        if (canWriteNestedTypeAnnotation(curPathBuilder + nestedType + '$', nestedTypes.subList(i + 1, nestedTypes.size()))) {\n          List<TypeAnnotationWriteHelper> notWrittenTypeAnnotations = writeNestedTypeAnnotations(sb, typeAnnWriteHelpers);\n          shouldWrite |= (notWrittenTypeAnnotations.size() != typeAnnWriteHelpers.size());\n          typeAnnWriteHelpers = notWrittenTypeAnnotations;\n          if (i != nestedTypes.size() - 1) popNestedTypeAnnotation(typeAnnWriteHelpers);\n        }\n      }\n      if (shouldWrite) {\n        sb.append(nestedType);\n        curPathBuilder.append(nestedType);\n        if (i != nestedTypes.size() - 1) {\n          curPathBuilder.append('$');\n          sb.append('.');\n        }\n      }\n    }\n    return typeAnnWriteHelpers;\n  }\n\n  /**\n   * Nested type annotations can only be written when all types on the right of the currently annotated type don't reference a static class.\n   */\n  public static boolean canWriteNestedTypeAnnotation(String curPath, List<String> nestedTypes) {\n    if (nestedTypes.isEmpty()) return true;\n    String fullName = curPath + nestedTypes.get(0);\n    ClassesProcessor.ClassNode classNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(fullName);\n    if (classNode == null) return false;\n    return (classNode.access & CodeConstants.ACC_STATIC) == 0 && canWriteNestedTypeAnnotation(fullName + \"$\", nestedTypes.subList(1, nestedTypes.size()));\n  }\n\n  public static List<ClassesProcessor.ClassNode> enclosingClassList() {\n    ClassesProcessor.ClassNode enclosingClass = (ClassesProcessor.ClassNode) DecompilerContext.getProperty(\n      DecompilerContext.CURRENT_CLASS_NODE\n    );\n    List<ClassesProcessor.ClassNode> enclosingClassList = new ArrayList<>(List.of(enclosingClass));\n    while (enclosingClass.parent != null) {\n      enclosingClass = enclosingClass.parent;\n      enclosingClassList.add(0, enclosingClass);\n    }\n    return enclosingClassList.stream()\n      .filter(classNode -> classNode.type != ClassesProcessor.ClassNode.CLASS_ANONYMOUS &&\n                           classNode.type != ClassesProcessor.ClassNode.CLASS_LAMBDA\n      ).collect(Collectors.toList());\n  }\n\n  public static List<TypeAnnotationWriteHelper> writeNestedTypeAnnotations(\n    StringBuilder sb,\n    List<TypeAnnotationWriteHelper> typeAnnWriteHelpers\n  ) {\n    return typeAnnWriteHelpers.stream().filter(typeAnnWriteHelper -> {\n      StructTypePathEntry path = typeAnnWriteHelper.getPaths().peek();\n      if (path == null) {\n        typeAnnWriteHelper.writeTo(sb);\n        return false;\n      }\n      return true;\n    }).collect(Collectors.toList());\n  }\n\n  /**\n   * Pops the nested path entry of the type annotation helper stack. Should be called after writing a nested type.\n   */\n  public static void popNestedTypeAnnotation(List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    typeAnnWriteHelpers.forEach(typeAnnWriteHelper -> {\n      StructTypePathEntry path = typeAnnWriteHelper.getPaths().peek();\n      if (path != null && path.getTypePathEntryKind() == StructTypePathEntry.Kind.NESTED.getId()) {\n        typeAnnWriteHelper.getPaths().pop();\n      }\n    });\n  }\n\n  public static String getCastTypeName(VarType type, List<TypeAnnotationWriteHelper> typePathWriteHelper) {\n    return getCastTypeName(type, true, typePathWriteHelper);\n  }\n\n  public static String getCastTypeName(VarType type, boolean getShort, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    List<TypeAnnotationWriteHelper> arrayTypeAnnWriteHelpers = arrayPath(type, typeAnnWriteHelpers);\n    List<TypeAnnotationWriteHelper> nonArrayTypeAnnWriteHelpers = nonArrayPath(type, typeAnnWriteHelpers);\n    StringBuilder sb = new StringBuilder(getTypeName(type, getShort, nonArrayTypeAnnWriteHelpers));\n    writeArray(sb, type.getArrayDim(), arrayTypeAnnWriteHelpers);\n    return sb.toString();\n  }\n\n\n  public static List<TypeAnnotationWriteHelper> arrayPath(Type type, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    return typeAnnWriteHelpers.stream()\n      .filter(typeAnnWriteHelper -> typeAnnWriteHelper.getPaths().size() < type.getArrayDim())\n      .collect(Collectors.toList());\n  }\n\n  public static List<TypeAnnotationWriteHelper> nonArrayPath(Type type, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    return typeAnnWriteHelpers.stream().filter(stack -> {\n      boolean isArrayPath = stack.getPaths().size() < type.getArrayDim();\n      if (stack.getPaths().size() > type.getArrayDim()) {\n        for (int i = 0; i < type.getArrayDim(); i++) {\n          stack.getPaths().poll(); // remove all trailing\n        }\n      }\n      return !isArrayPath;\n    }).collect(Collectors.toList());\n  }\n\n\n  public static void writeArray(StringBuilder sb, int arrayDim, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    for (int i = 0; i < arrayDim; i++) {\n      boolean firstIteration = true;\n      for (TypeAnnotationWriteHelper typeAnnotationWriteHelper : typeAnnWriteHelpers) {\n        if (i == typeAnnotationWriteHelper.getPaths().size()) {\n          if (firstIteration) {\n            sb.append(' ');\n            firstIteration = false;\n          }\n          typeAnnotationWriteHelper.writeTo(sb);\n        }\n      }\n      sb.append(\"[]\");\n    }\n  }\n\n  public static PrimitiveExpressionList getExpressionData(VarExprent var) {\n    PrimitiveExpressionList expressionList = new PrimitiveExpressionList();\n    VarExprent varTmp = new VarExprent(VarExprent.STACK_BASE, var.getExprType(), var.getProcessor());\n    varTmp.setStack(true);\n\n    expressionList.getExpressions().add(new AssignmentExprent(varTmp, var.copy(), null));\n    expressionList.getStack().push(varTmp.copy());\n    return expressionList;\n  }\n\n  public static boolean endsWithSemicolon(Exprent expr) {\n    int type = expr.type;\n    return !(type == Exprent.EXPRENT_SWITCH ||\n             type == Exprent.EXPRENT_MONITOR ||\n             type == Exprent.EXPRENT_IF ||\n             (type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef()));\n  }\n\n  private static void addDeletedGotoInstructionMapping(Statement stat, BytecodeMappingTracer tracer) {\n    if (stat instanceof BasicBlockStatement) {\n      BasicBlock block = ((BasicBlockStatement)stat).getBlock();\n      List<Integer> offsets = block.getOriginalOffsets();\n      if (!offsets.isEmpty() &&\n          offsets.size() > block.getSeq().length()) { // some instructions have been deleted, but we still have offsets\n        tracer.addMapping(offsets.get(offsets.size() - 1)); // add the last offset\n      }\n    }\n  }\n\n  public static TextBuffer jmpWrapper(Statement stat, int indent, boolean semicolon, BytecodeMappingTracer tracer) {\n    TextBuffer buf = stat.toJava(indent, tracer);\n\n    List<StatEdge> successorEdges = stat.getSuccessorEdges(EdgeType.DIRECT_ALL);\n    if (successorEdges.size() == 1) {\n      StatEdge edge = successorEdges.get(0);\n      if (edge.getType() != EdgeType.REGULAR && edge.explicit && edge.getDestination().type != StatementType.DUMMY_EXIT) {\n        buf.appendIndent(indent);\n\n        if (EdgeType.BREAK.equals(edge.getType())) {\n          addDeletedGotoInstructionMapping(stat, tracer);\n          buf.append(\"break\");\n        }\n        else if (EdgeType.CONTINUE.equals(edge.getType())) {\n          addDeletedGotoInstructionMapping(stat, tracer);\n          buf.append(\"continue\");\n        }\n\n        if (edge.labeled) {\n          buf.append(\" label\").append(Integer.toString(edge.closure.id));\n        }\n        buf.append(\";\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n    }\n\n    if (buf.length() == 0 && semicolon) {\n      buf.appendIndent(indent).append(\";\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    return buf;\n  }\n\n  public static String buildJavaClassName(String name) {\n    String res = name.replace('/', '.');\n\n    if (res.contains(\"$\")) { // attempt to invoke foreign member\n      // classes correctly\n      StructClass cl = DecompilerContext.getStructContext().getClass(name);\n      if (cl == null || !cl.isOwn()) {\n        res = res.replace('$', '.');\n      }\n    }\n\n    return res;\n  }\n\n  public static TextBuffer listToJava(List<? extends Exprent> lst, int indent, BytecodeMappingTracer tracer) {\n    if (lst == null || lst.isEmpty()) {\n      return new TextBuffer();\n    }\n\n    TextBuffer buf = new TextBuffer();\n\n    for (Exprent expr : lst) {\n      if (buf.length() > 0 && expr.type == Exprent.EXPRENT_VAR && ((VarExprent)expr).isClassDef()) {\n        // separates local class definition from previous statements\n        buf.appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n\n      TextBuffer content = expr.toJava(indent, tracer);\n\n      if (content.length() > 0) {\n        if (expr.type != Exprent.EXPRENT_VAR || !((VarExprent)expr).isClassDef()) {\n          buf.appendIndent(indent);\n        }\n        buf.append(content);\n        if (expr.type == Exprent.EXPRENT_MONITOR && ((MonitorExprent)expr).getMonType() == MonitorExprent.MONITOR_ENTER) {\n          buf.append(\"{}\"); // empty synchronized block\n        }\n        if (endsWithSemicolon(expr)) {\n          buf.append(\";\");\n        }\n        buf.appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n    }\n\n    return buf;\n  }\n\n  public static ConstExprent getDefaultArrayValue(VarType arrType) {\n    ConstExprent defaultVal;\n    if (arrType.getType() == CodeConstants.TYPE_OBJECT || arrType.getArrayDim() > 0) {\n      defaultVal = new ConstExprent(VarType.VARTYPE_NULL, null, null);\n    }\n    else if (arrType.getType() == CodeConstants.TYPE_FLOAT) {\n      defaultVal = new ConstExprent(VarType.VARTYPE_FLOAT, 0f, null);\n    }\n    else if (arrType.getType() == CodeConstants.TYPE_LONG) {\n      defaultVal = new ConstExprent(VarType.VARTYPE_LONG, 0L, null);\n    }\n    else if (arrType.getType() == CodeConstants.TYPE_DOUBLE) {\n      defaultVal = new ConstExprent(VarType.VARTYPE_DOUBLE, 0d, null);\n    }\n    else { // integer types\n      defaultVal = new ConstExprent(0, true, null);\n    }\n    return defaultVal;\n  }\n\n  public static boolean getCastedExprent(Exprent exprent,\n                                         VarType leftType,\n                                         TextBuffer buffer,\n                                         int indent,\n                                         boolean castNull,\n                                         BytecodeMappingTracer tracer) {\n    return getCastedExprent(exprent, leftType, buffer, indent, castNull, false, false, false, tracer);\n  }\n\n  public static boolean getCastedExprent(Exprent exprent,\n                                         VarType leftType,\n                                         TextBuffer buffer,\n                                         int indent,\n                                         boolean castNull,\n                                         boolean castAlways,\n                                         boolean castNarrowing,\n                                         boolean unbox,\n                                         BytecodeMappingTracer tracer) {\n\n    if (unbox) {\n      // \"unbox\" invocation parameters, e.g. 'byteSet.add((byte)123)' or 'new ShortContainer((short)813)'\n      if (exprent.type == Exprent.EXPRENT_INVOCATION && ((InvocationExprent)exprent).isBoxingCall()) {\n        InvocationExprent invocationExprent = (InvocationExprent)exprent;\n        exprent = invocationExprent.getParameters().get(0);\n        int paramType = invocationExprent.getDescriptor().params[0].getType();\n        if (exprent.type == Exprent.EXPRENT_CONST && ((ConstExprent)exprent).getConstType().getType() != paramType) {\n          leftType = new VarType(paramType);\n        }\n      }\n    }\n\n    VarType rightType = exprent.getExprType();\n\n    boolean cast =\n      castAlways ||\n      (!leftType.isSuperset(rightType) && (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.getType() != CodeConstants.TYPE_OBJECT)) ||\n      (castNull && rightType.getType() == CodeConstants.TYPE_NULL && !UNDEFINED_TYPE_STRING.equals(getTypeName(leftType, Collections.emptyList()))) ||\n      (castNarrowing && isIntConstant(exprent) && isNarrowedIntType(leftType));\n\n    boolean quote = cast && exprent.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST);\n\n    // cast instead to 'byte' / 'short' when int constant is used as a value for 'Byte' / 'Short'\n    if (castNarrowing && exprent.type == Exprent.EXPRENT_CONST && !((ConstExprent) exprent).isNull()) {\n      if (leftType.equals(VarType.VARTYPE_BYTE_OBJ)) {\n        leftType = VarType.VARTYPE_BYTE;\n      }\n      else if (leftType.equals(VarType.VARTYPE_SHORT_OBJ)) {\n        leftType = VarType.VARTYPE_SHORT;\n      }\n    }\n\n    if (cast) buffer.append('(').append(getCastTypeName(leftType, Collections.emptyList())).append(')');\n\n    if (quote) buffer.append('(');\n\n    if (exprent.type == Exprent.EXPRENT_CONST) {\n      ((ConstExprent) exprent).adjustConstType(leftType);\n    }\n\n    buffer.append(exprent.toJava(indent, tracer));\n\n    if (quote) buffer.append(')');\n\n    return cast;\n  }\n\n  private static boolean isIntConstant(Exprent exprent) {\n    if (exprent.type == Exprent.EXPRENT_CONST) {\n      switch (((ConstExprent)exprent).getConstType().getType()) {\n        case CodeConstants.TYPE_BYTE, CodeConstants.TYPE_BYTECHAR, CodeConstants.TYPE_SHORT,\n          CodeConstants.TYPE_SHORTCHAR, CodeConstants.TYPE_INT -> {\n          return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static boolean isNarrowedIntType(VarType type) {\n    return VarType.VARTYPE_INT.isStrictSuperset(type) || type.equals(VarType.VARTYPE_BYTE_OBJ) || type.equals(VarType.VARTYPE_SHORT_OBJ);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/ExpressionStack.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.util.ListStack;\n\npublic class ExpressionStack extends ListStack<Exprent> {\n  public ExpressionStack() { }\n\n  private ExpressionStack(int initialCapacity) {\n    super(initialCapacity);\n  }\n\n  @Override\n  public ExpressionStack copy() {\n    ExpressionStack copy = new ExpressionStack(size());\n    for (Exprent expr : this) copy.push(expr.copy());\n    return copy;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.code.SimpleInstructionSequence;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;\nimport org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.code.DeadCodeHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class FinallyProcessor {\n  private final Map<Integer, Integer> finallyBlockIDs = new HashMap<>();\n  private final Map<Integer, Integer> catchallBlockIDs = new HashMap<>();\n\n  private final MethodDescriptor methodDescriptor;\n  private final VarProcessor varProcessor;\n\n  public FinallyProcessor(MethodDescriptor md, VarProcessor varProc) {\n    methodDescriptor = md;\n    varProcessor = varProc;\n  }\n\n  public boolean iterateGraph(StructClass cl, StructMethod mt, RootStatement root, ControlFlowGraph graph) {\n    int bytecodeVersion = mt.getBytecodeVersion();\n\n    LinkedList<Statement> stack = new LinkedList<>();\n    stack.add(root);\n\n    while (!stack.isEmpty()) {\n      Statement stat = stack.removeLast();\n\n      Statement parent = stat.getParent();\n      if (parent != null && parent.type == StatementType.CATCH_ALL &&\n          stat == parent.getFirst() && !parent.isCopied()) {\n\n        CatchAllStatement fin = (CatchAllStatement)parent;\n        BasicBlock head = fin.getBasichead().getBlock();\n        BasicBlock handler = fin.getHandler().getBasichead().getBlock();\n\n        if (catchallBlockIDs.containsKey(handler.id)) {\n          // do nothing\n        }\n        else if (finallyBlockIDs.containsKey(handler.id)) {\n          fin.setFinally(true);\n\n          Integer var = finallyBlockIDs.get(handler.id);\n          fin.setMonitor(var == null ? null : new VarExprent(var, VarType.VARTYPE_INT, varProcessor));\n        }\n        else {\n          Record inf = getFinallyInformation(cl, mt, root, fin);\n\n          if (inf == null) { // inconsistent finally\n            catchallBlockIDs.put(handler.id, null);\n          }\n          else {\n            if (DecompilerContext.getOption(IFernflowerPreferences.FINALLY_DEINLINE) && verifyFinallyEx(graph, fin, inf)) {\n              finallyBlockIDs.put(handler.id, null);\n            }\n            else {\n              int varIndex = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);\n              insertSemaphore(graph, getAllBasicBlocks(fin.getFirst()), head, handler, varIndex, inf, bytecodeVersion);\n\n              finallyBlockIDs.put(handler.id, varIndex);\n            }\n\n            DeadCodeHelper.removeDeadBlocks(graph); // e.g. multiple return blocks after a nested finally\n            DeadCodeHelper.removeEmptyBlocks(graph);\n            DeadCodeHelper.mergeBasicBlocks(graph);\n          }\n\n          return true;\n        }\n      }\n\n      stack.addAll(stat.getStats());\n    }\n\n    return false;\n  }\n\n  private static final class Record {\n    private final int firstCode;\n    private final Map<BasicBlock, Boolean> mapLast;\n\n    private Record(int firstCode, Map<BasicBlock, Boolean> mapLast) {\n      this.firstCode = firstCode;\n      this.mapLast = mapLast;\n    }\n  }\n\n  private Record getFinallyInformation(StructClass cl, StructMethod mt, RootStatement root, CatchAllStatement fstat) {\n    Map<BasicBlock, Boolean> mapLast = new HashMap<>();\n\n    BasicBlockStatement firstBlockStatement = fstat.getHandler().getBasichead();\n    BasicBlock firstBasicBlock = firstBlockStatement.getBlock();\n    Instruction instrFirst = firstBasicBlock.getInstruction(0);\n\n    int firstCode = switch (instrFirst.opcode) {\n      case CodeConstants.opc_pop -> 1;\n      case CodeConstants.opc_astore -> 2;\n      default -> 0;\n    };\n\n    ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor);\n    proc.processStatement(root, cl);\n\n    SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();\n    ssa.splitVariables(root, mt);\n\n    List<Exprent> expressions = firstBlockStatement.getExprents();\n\n    VarVersionPair pair = new VarVersionPair((VarExprent)((AssignmentExprent)expressions.get(firstCode == 2 ? 1 : 0)).getLeft());\n\n    FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper();\n    DirectGraph dgraph = flattenHelper.buildDirectGraph(root);\n\n    LinkedList<DirectNode> stack = new LinkedList<>();\n    stack.add(dgraph.first);\n\n    Set<DirectNode> setVisited = new HashSet<>();\n\n    while (!stack.isEmpty()) {\n      DirectNode node = stack.removeFirst();\n\n      if (setVisited.contains(node)) {\n        continue;\n      }\n      setVisited.add(node);\n\n      BasicBlockStatement blockStatement = null;\n      if (node.block != null) {\n        blockStatement = node.block;\n      }\n      else if (node.predecessors.size() == 1) {\n        blockStatement = node.predecessors.get(0).block;\n      }\n\n      boolean isTrueExit = true;\n\n      if (firstCode != 1) {\n        isTrueExit = false;\n\n        for (int i = 0; i < node.exprents.size(); i++) {\n          Exprent exprent = node.exprents.get(i);\n\n          if (firstCode == 0) {\n            List<Exprent> lst = exprent.getAllExprents();\n            lst.add(exprent);\n\n            boolean found = false;\n            for (Exprent expr : lst) {\n              if (expr.type == Exprent.EXPRENT_VAR && new VarVersionPair((VarExprent)expr).equals(pair)) {\n                found = true;\n                break;\n              }\n            }\n\n            if (found) {\n              found = false;\n              if (exprent.type == Exprent.EXPRENT_EXIT) {\n                ExitExprent exit = (ExitExprent)exprent;\n                if (exit.getExitType() == ExitExprent.EXIT_THROW && exit.getValue().type == Exprent.EXPRENT_VAR) {\n                  found = true;\n                }\n              }\n\n              if (!found) {\n                return null;\n              }\n              else {\n                isTrueExit = true;\n              }\n            }\n          }\n          else {  // firstCode == 2\n            // searching for a load instruction\n            if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n              AssignmentExprent assignment = (AssignmentExprent)exprent;\n              if (assignment.getRight().type == Exprent.EXPRENT_VAR &&\n                  new VarVersionPair((VarExprent)assignment.getRight()).equals(pair)) {\n                Exprent next = null;\n                if (i == node.exprents.size() - 1) {\n                  if (node.successors.size() == 1) {\n                    DirectNode nd = node.successors.get(0);\n                    if (!nd.exprents.isEmpty()) {\n                      next = nd.exprents.get(0);\n                    }\n                  }\n                }\n                else {\n                  next = node.exprents.get(i + 1);\n                }\n\n                boolean found = false;\n                if (next != null && next.type == Exprent.EXPRENT_EXIT) {\n                  ExitExprent exit = (ExitExprent)next;\n                  if (exit.getExitType() == ExitExprent.EXIT_THROW && exit.getValue().type == Exprent.EXPRENT_VAR &&\n                      assignment.getLeft().equals(exit.getValue())) {\n                    found = true;\n                  }\n                }\n\n                if (!found) {\n                  return null;\n                }\n                else {\n                  isTrueExit = true;\n                }\n              }\n            }\n          }\n        }\n      }\n\n      // find finally exits\n      if (blockStatement != null && blockStatement.getBlock() != null) {\n        Statement handler = fstat.getHandler();\n        for (StatEdge edge : blockStatement.getSuccessorEdges(EdgeType.DIRECT_ALL)) {\n          if (edge.getType() != EdgeType.REGULAR && handler.containsStatement(blockStatement)\n              && !handler.containsStatement(edge.getDestination())) {\n            Boolean existingFlag = mapLast.get(blockStatement.getBlock());\n            // note: the dummy node is also processed!\n            if (existingFlag == null || !existingFlag) {\n              mapLast.put(blockStatement.getBlock(), isTrueExit);\n              break;\n            }\n          }\n        }\n      }\n\n      stack.addAll(node.successors);\n    }\n\n    // an empty `finally` block?\n    if (fstat.getHandler().type == StatementType.BASIC_BLOCK) {\n      boolean isFirstLast = mapLast.containsKey(firstBasicBlock);\n      InstructionSequence seq = firstBasicBlock.getSeq();\n\n      boolean isEmpty = switch (firstCode) {\n        case 0 -> isFirstLast && seq.length() == 1;\n        case 1 -> seq.length() == 1;\n        case 2 -> isFirstLast ? seq.length() == 3 : seq.length() == 1;\n        default -> false;\n      };\n\n      if (isEmpty) {\n        firstCode = 3;\n      }\n    }\n\n    return new Record(firstCode, mapLast);\n  }\n\n  private static void insertSemaphore(ControlFlowGraph graph,\n                                      Set<BasicBlock> setTry,\n                                      BasicBlock head,\n                                      BasicBlock handler,\n                                      int var,\n                                      Record information,\n                                      int bytecodeVersion) {\n    Set<BasicBlock> setCopy = new HashSet<>(setTry);\n\n    int finallyType = information.firstCode;\n    Map<BasicBlock, Boolean> mapLast = information.mapLast;\n\n    // first and last statements\n    removeExceptionInstructionsEx(handler, 1, finallyType);\n    for (Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) {\n      BasicBlock last = entry.getKey();\n\n      if (entry.getValue()) {\n        removeExceptionInstructionsEx(last, 2, finallyType);\n        graph.getFinallyExits().add(last);\n      }\n    }\n\n    // disable semaphore at statement exit points\n    for (BasicBlock block : setTry) {\n      for (BasicBlock dest : block.getSuccessors()) {\n        // break out\n        if (dest != graph.getLast() && !setCopy.contains(dest)) {\n          // disable semaphore\n          SimpleInstructionSequence seq = new SimpleInstructionSequence();\n          seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecodeVersion, new int[]{0}), -1);\n          seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecodeVersion, new int[]{var}), -1);\n\n          // build a separate block\n          BasicBlock newBlock = new BasicBlock(++graph.last_id, seq);\n\n          // insert between block and dest\n          block.replaceSuccessor(dest, newBlock);\n          newBlock.addSuccessor(dest);\n          setCopy.add(newBlock);\n          graph.getBlocks().addWithKey(newBlock, newBlock.id);\n\n          // exception ranges\n          // FIXME: special case synchronized\n          copyExceptionEdges(graph, block, newBlock);\n        }\n      }\n    }\n\n    // enable semaphore at the statement entrance\n    BasicBlock newHead = createHeadBlock(graph, 1, var, bytecodeVersion);\n    insertBlockBefore(graph, head, newHead);\n\n    // initialize semaphore with false\n    BasicBlock newHeadInit = createHeadBlock(graph, 0, var, bytecodeVersion);\n    insertBlockBefore(graph, newHead, newHeadInit);\n\n    setCopy.add(newHead);\n    setCopy.add(newHeadInit);\n\n    for (BasicBlock hd : new HashSet<>(newHeadInit.getSuccessorExceptions())) {\n      ExceptionRangeCFG range = graph.getExceptionRange(hd, newHeadInit);\n\n      if (setCopy.containsAll(range.getProtectedRange())) {\n        newHeadInit.removeSuccessorException(hd);\n        range.getProtectedRange().remove(newHeadInit);\n      }\n    }\n  }\n\n  @NotNull\n  private static BasicBlock createHeadBlock(ControlFlowGraph graph, int value, int var, int bytecodeVersion) {\n    SimpleInstructionSequence seq = new SimpleInstructionSequence();\n    seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecodeVersion, new int[]{value}), -1);\n    seq.addInstruction(Instruction.create(CodeConstants.opc_istore, false, CodeConstants.GROUP_GENERAL, bytecodeVersion, new int[]{var}), -1);\n    return new BasicBlock(++graph.last_id, seq);\n  }\n\n  private static void insertBlockBefore(ControlFlowGraph graph, BasicBlock oldBlock, BasicBlock newBlock) {\n    List<BasicBlock> blocks = new ArrayList<>();\n    blocks.addAll(oldBlock.getPredecessors());\n    blocks.addAll(oldBlock.getPredecessorExceptions());\n\n    // replace predecessors\n    for (BasicBlock predecessor : blocks) {\n      predecessor.replaceSuccessor(oldBlock, newBlock);\n    }\n\n    // copy exception edges and extend protected ranges\n    for (BasicBlock hd : oldBlock.getSuccessorExceptions()) {\n      newBlock.addSuccessorException(hd);\n\n      ExceptionRangeCFG range = graph.getExceptionRange(hd, oldBlock);\n      range.getProtectedRange().add(newBlock);\n    }\n\n    // replace handler\n    for (ExceptionRangeCFG range : graph.getExceptions()) {\n      if (range.getHandler() == oldBlock) {\n        range.setHandler(newBlock);\n      }\n    }\n\n    newBlock.addSuccessor(oldBlock);\n    graph.getBlocks().addWithKey(newBlock, newBlock.id);\n    if (graph.getFirst() == oldBlock) {\n      graph.setFirst(newBlock);\n    }\n  }\n\n  private static Set<BasicBlock> getAllBasicBlocks(Statement stat) {\n    List<Statement> lst = new LinkedList<>();\n    lst.add(stat);\n\n    int index = 0;\n    do {\n      Statement st = lst.get(index);\n      if (st.type == StatementType.BASIC_BLOCK) {\n        index++;\n      }\n      else {\n        lst.addAll(st.getStats());\n        lst.remove(index);\n      }\n    }\n    while (index < lst.size());\n\n    Set<BasicBlock> res = new HashSet<>();\n    for (Statement st : lst) {\n      res.add(((BasicBlockStatement)st).getBlock());\n    }\n    return res;\n  }\n\n  private boolean verifyFinallyEx(ControlFlowGraph graph, CatchAllStatement fstat, Record information) {\n    Set<BasicBlock> tryBlocks = getAllBasicBlocks(fstat.getFirst());\n    Set<BasicBlock> catchBlocks = getAllBasicBlocks(fstat.getHandler());\n\n    int finallyType = information.firstCode;\n    Map<BasicBlock, Boolean> mapLast = information.mapLast;\n\n    BasicBlock first = fstat.getHandler().getBasichead().getBlock();\n    boolean skippedFirst = false;\n\n    if (finallyType == 3) {\n      // empty finally\n      removeExceptionInstructionsEx(first, 3, finallyType);\n\n      if (mapLast.containsKey(first)) {\n        graph.getFinallyExits().add(first);\n      }\n\n      return true;\n    }\n    else {\n      if (first.getSeq().length() == 1 && finallyType > 0) {\n        BasicBlock firstSuccessor = first.getSuccessors().get(0);\n        if (catchBlocks.contains(firstSuccessor)) {\n          first = firstSuccessor;\n          skippedFirst = true;\n        }\n      }\n    }\n\n    // identify start blocks\n    Set<BasicBlock> startBlocks = new HashSet<>();\n    for (BasicBlock block : tryBlocks) {\n      startBlocks.addAll(block.getSuccessors());\n    }\n    // `throw` in the `try` body will point directly to the dummy exit, so remove it\n    startBlocks.remove(graph.getLast());\n    startBlocks.removeAll(tryBlocks);\n\n    List<Area> areas = new ArrayList<>();\n    for (BasicBlock start : startBlocks) {\n      Area arr = compareSubGraphsEx(graph, start, catchBlocks, first, finallyType, mapLast, skippedFirst);\n      if (arr == null) {\n        return false;\n      }\n      areas.add(arr);\n    }\n\n    // delete areas\n    for (Area area : areas) {\n      deleteArea(graph, area);\n    }\n\n    // INFO: Empty basic blocks may remain in the graph!\n    for (Entry<BasicBlock, Boolean> entry : mapLast.entrySet()) {\n      BasicBlock last = entry.getKey();\n\n      if (entry.getValue()) {\n        removeExceptionInstructionsEx(last, 2, finallyType);\n        graph.getFinallyExits().add(last);\n      }\n    }\n\n    removeExceptionInstructionsEx(fstat.getHandler().getBasichead().getBlock(), 1, finallyType);\n\n    return true;\n  }\n\n  private static final class Area {\n    private final BasicBlock start;\n    private final Set<BasicBlock> sample;\n    private final BasicBlock next;\n\n    private Area(BasicBlock start, Set<BasicBlock> sample, BasicBlock next) {\n      this.start = start;\n      this.sample = sample;\n      this.next = next;\n    }\n  }\n\n  private Area compareSubGraphsEx(ControlFlowGraph graph,\n                                  BasicBlock startSample,\n                                  Set<BasicBlock> catchBlocks,\n                                  BasicBlock startCatch,\n                                  int finallyType,\n                                  Map<BasicBlock, Boolean> mapLast,\n                                  boolean skippedFirst) {\n    class BlockStackEntry {\n      public final BasicBlock blockCatch;\n      public final BasicBlock blockSample;\n\n      // TODO: correct handling (merging) of multiple paths\n      public final List<int[]> lstStoreVars;\n\n      BlockStackEntry(BasicBlock blockCatch, BasicBlock blockSample, List<int[]> lstStoreVars) {\n        this.blockCatch = blockCatch;\n        this.blockSample = blockSample;\n        this.lstStoreVars = new ArrayList<>(lstStoreVars);\n      }\n    }\n\n    List<BlockStackEntry> stack = new LinkedList<>();\n\n    Set<BasicBlock> setSample = new HashSet<>();\n\n    Map<String, BasicBlock[]> mapNext = new HashMap<>();\n\n    stack.add(new BlockStackEntry(startCatch, startSample, new ArrayList<>()));\n\n    while (!stack.isEmpty()) {\n      BlockStackEntry entry = stack.remove(0);\n      BasicBlock blockCatch = entry.blockCatch;\n      BasicBlock blockSample = entry.blockSample;\n\n      boolean isFirstBlock = !skippedFirst && blockCatch == startCatch;\n      boolean isLastBlock = mapLast.containsKey(blockCatch);\n      boolean isTrueLastBlock = isLastBlock && mapLast.get(blockCatch);\n\n      int compareType = (isFirstBlock ? 1 : 0) | (isTrueLastBlock ? 2 : 0);\n      if (!compareBasicBlocksEx(graph, blockCatch, blockSample, compareType, finallyType, entry.lstStoreVars)) {\n        return null;\n      }\n\n      if (blockSample.getSuccessors().size() != blockCatch.getSuccessors().size()) {\n        return null;\n      }\n\n      setSample.add(blockSample);\n\n      // direct successors\n      for (int i = 0; i < blockCatch.getSuccessors().size(); i++) {\n        BasicBlock sucCatch = blockCatch.getSuccessors().get(i);\n        BasicBlock sucSample = blockSample.getSuccessors().get(i);\n\n        if (catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) {\n          stack.add(new BlockStackEntry(sucCatch, sucSample, entry.lstStoreVars));\n        }\n      }\n\n      // exception successors\n      if (isLastBlock && blockSample.getSeq().isEmpty()) {\n        // do nothing, blockSample will be removed anyway\n      }\n      else {\n        if (blockCatch.getSuccessorExceptions().size() == blockSample.getSuccessorExceptions().size()) {\n          for (int i = 0; i < blockCatch.getSuccessorExceptions().size(); i++) {\n            BasicBlock sucCatch = blockCatch.getSuccessorExceptions().get(i);\n            BasicBlock sucSample = blockSample.getSuccessorExceptions().get(i);\n\n            String excCatch = graph.getExceptionRange(sucCatch, blockCatch).getUniqueExceptionsString();\n            String excSample = graph.getExceptionRange(sucSample, blockSample).getUniqueExceptionsString();\n\n            // FIXME: compare handlers if possible\n            if (Objects.equals(excCatch, excSample)) {\n              if (catchBlocks.contains(sucCatch) && !setSample.contains(sucSample)) {\n                List<int[]> lst = entry.lstStoreVars;\n\n                if (sucCatch.getSeq().length() > 0 && sucSample.getSeq().length() > 0) {\n                  Instruction instrCatch = sucCatch.getSeq().getInstr(0);\n                  Instruction instrSample = sucSample.getSeq().getInstr(0);\n\n                  if (instrCatch.opcode == CodeConstants.opc_astore &&\n                      instrSample.opcode == CodeConstants.opc_astore) {\n                    lst = new ArrayList<>(lst);\n                    lst.add(new int[]{instrCatch.operand(0), instrSample.operand(0)});\n                  }\n                }\n\n                stack.add(new BlockStackEntry(sucCatch, sucSample, lst));\n              }\n            }\n            else {\n              return null;\n            }\n          }\n        }\n        else {\n          return null;\n        }\n      }\n\n      if (isLastBlock) {\n        Set<BasicBlock> successors = new HashSet<>(blockSample.getSuccessors());\n        successors.removeAll(setSample);\n\n        for (BlockStackEntry stackEntry : stack) {\n          successors.remove(stackEntry.blockSample);\n        }\n\n        for (BasicBlock successor : successors) {\n          if (graph.getLast() != successor) { // FIXME: why?\n            mapNext.put(blockSample.id + \"#\" + successor.id, new BasicBlock[]{blockSample, successor, isTrueLastBlock ? successor : null});\n          }\n        }\n      }\n    }\n\n    return new Area(startSample, setSample, getUniqueNext(graph, new HashSet<>(mapNext.values())));\n  }\n\n  private static BasicBlock getUniqueNext(ControlFlowGraph graph, Set<BasicBlock[]> setNext) {\n    // precondition: there is at most one true exit path in the `finally` statement\n    BasicBlock next = null;\n    boolean multiple = false;\n\n    for (BasicBlock[] arr : setNext) {\n      if (arr[2] != null) {\n        next = arr[1];\n        multiple = false;\n        break;\n      }\n      else {\n        if (next == null) {\n          next = arr[1];\n        }\n        else if (next != arr[1]) {\n          multiple = true;\n        }\n\n        if (arr[1].getPredecessors().size() == 1) {\n          next = arr[1];\n        }\n      }\n    }\n\n    if (multiple) { // TODO: generic solution\n      for (BasicBlock[] arr : setNext) {\n        BasicBlock block = arr[1];\n\n        if (block != next) {\n          if (InterpreterUtil.equalSets(next.getSuccessors(), block.getSuccessors())) {\n            InstructionSequence seqNext = next.getSeq();\n            InstructionSequence seqBlock = block.getSeq();\n\n            if (seqNext.length() == seqBlock.length()) {\n              for (int i = 0; i < seqNext.length(); i++) {\n                Instruction instrNext = seqNext.getInstr(i);\n                Instruction instrBlock = seqBlock.getInstr(i);\n\n                if (!Instruction.equals(instrNext, instrBlock)) {\n                  return null;\n                }\n                for (int j = 0; j < instrNext.operandsCount(); j++) {\n                  if (instrNext.operand(j) != instrBlock.operand(j)) {\n                    return null;\n                  }\n                }\n              }\n            }\n            else {\n              return null;\n            }\n          }\n          else {\n            return null;\n          }\n        }\n      }\n      for (BasicBlock[] arr : setNext) {\n        if (arr[1] != next) {\n          // FIXME: exception edge possible?\n          arr[0].removeSuccessor(arr[1]);\n          arr[0].addSuccessor(next);\n        }\n      }\n\n      DeadCodeHelper.removeDeadBlocks(graph);\n    }\n\n    return next;\n  }\n\n  private boolean compareBasicBlocksEx(ControlFlowGraph graph,\n                                       BasicBlock pattern,\n                                       BasicBlock sample,\n                                       int type,\n                                       int finallyType,\n                                       List<int[]> lstStoreVars) {\n    InstructionSequence seqPattern = pattern.getSeq();\n    InstructionSequence seqSample = sample.getSeq();\n\n    if (type != 0) {\n      seqPattern = seqPattern.clone();\n\n      if ((type & 1) > 0) { // first\n        if (finallyType > 0) {\n          seqPattern.removeInstruction(0);\n        }\n      }\n\n      if ((type & 2) > 0) { // last\n        if (finallyType == 0 || finallyType == 2) {\n          seqPattern.removeLast();\n        }\n\n        if (finallyType == 2) {\n          seqPattern.removeLast();\n        }\n      }\n    }\n\n    if (seqPattern.length() > seqSample.length()) {\n      return false;\n    }\n\n    for (int i = 0; i < seqPattern.length(); i++) {\n      Instruction instrPattern = seqPattern.getInstr(i);\n      Instruction instrSample = seqSample.getInstr(i);\n\n      // compare instructions with respect to jumps\n      if (!equalInstructions(instrPattern, instrSample, lstStoreVars)) {\n        return false;\n      }\n    }\n\n    if (seqPattern.length() < seqSample.length()) { // split in two blocks\n      SimpleInstructionSequence seq = new SimpleInstructionSequence();\n      LinkedList<Integer> oldOffsets = new LinkedList<>();\n      for (int i = seqSample.length() - 1; i >= seqPattern.length(); i--) {\n        seq.addInstruction(0, seqSample.getInstr(i), -1);\n        oldOffsets.addFirst(sample.getOriginalOffset(i));\n        seqSample.removeInstruction(i);\n      }\n\n      BasicBlock newBlock = new BasicBlock(++graph.last_id, seq);\n      newBlock.getOriginalOffsets().addAll(oldOffsets);\n\n      List<BasicBlock> lstTemp = new ArrayList<>(sample.getSuccessors());\n\n      // move successors\n      for (BasicBlock suc : lstTemp) {\n        sample.removeSuccessor(suc);\n        newBlock.addSuccessor(suc);\n      }\n\n      sample.addSuccessor(newBlock);\n\n      graph.getBlocks().addWithKey(newBlock, newBlock.id);\n\n      Set<BasicBlock> setFinallyExits = graph.getFinallyExits();\n      if (setFinallyExits.contains(sample)) {\n        setFinallyExits.remove(sample);\n        setFinallyExits.add(newBlock);\n      }\n\n      copyExceptionEdges(graph, sample, newBlock);\n    }\n\n    return true;\n  }\n\n  // copy exception edges and extend protected ranges\n  public static void copyExceptionEdges(ControlFlowGraph graph, BasicBlock sample, BasicBlock newBlock) {\n    for (int i = 0; i < sample.getSuccessorExceptions().size(); i++) {\n      BasicBlock hd = sample.getSuccessorExceptions().get(i);\n      newBlock.addSuccessorException(hd);\n      ExceptionRangeCFG range = graph.getExceptionRange(hd, sample);\n      range.getProtectedRange().add(newBlock);\n    }\n  }\n\n  public boolean equalInstructions(Instruction first, Instruction second, List<int[]> lstStoreVars) {\n    if (!Instruction.equals(first, second)) {\n      return false;\n    }\n\n    if (first.group != CodeConstants.GROUP_JUMP) { // FIXME: switch comparison\n      for (int i = 0; i < first.operandsCount(); i++) {\n        int firstOp = first.operand(i);\n        int secondOp = second.operand(i);\n        if (firstOp != secondOp) {\n          // a-load/store instructions\n          if (first.opcode == CodeConstants.opc_aload || first.opcode == CodeConstants.opc_astore) {\n            for (int[] arr : lstStoreVars) {\n              if (arr[0] == firstOp && arr[1] == secondOp) {\n                return true;\n              }\n            }\n          }\n\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n\n  private static void deleteArea(ControlFlowGraph graph, Area area) {\n    BasicBlock start = area.start;\n    BasicBlock next = area.next;\n\n    if (start == next) {\n      return;\n    }\n\n    if (next == null) {\n      // dummy exit block\n      next = graph.getLast();\n    }\n\n    // collecting common exception ranges of predecessors and successors\n    Set<BasicBlock> setCommonExceptionHandlers = new HashSet<>(next.getSuccessorExceptions());\n    for (BasicBlock predecessor : start.getPredecessors()) {\n      setCommonExceptionHandlers.retainAll(predecessor.getSuccessorExceptions());\n    }\n\n    boolean isOutsideRange = false;\n\n    Set<BasicBlock> setPredecessors = new HashSet<>(start.getPredecessors());\n\n    // replace start with next\n    for (BasicBlock predecessor : setPredecessors) {\n      predecessor.replaceSuccessor(start, next);\n    }\n\n    Set<BasicBlock> setBlocks = area.sample;\n\n    Set<ExceptionRangeCFG> setCommonRemovedExceptionRanges = null;\n\n    // remove all the blocks in between\n    for (BasicBlock block : setBlocks) {\n      // artificial basic blocks (those resulting from splitting) may belong to more than one area\n      if (graph.getBlocks().containsKey(block.id)) {\n        if (!new HashSet<>(block.getSuccessorExceptions()).containsAll(setCommonExceptionHandlers)) {\n          isOutsideRange = true;\n        }\n\n        Set<ExceptionRangeCFG> setRemovedExceptionRanges = new HashSet<>();\n        for (BasicBlock handler : block.getSuccessorExceptions()) {\n          setRemovedExceptionRanges.add(graph.getExceptionRange(handler, block));\n        }\n\n        if (setCommonRemovedExceptionRanges == null) {\n          setCommonRemovedExceptionRanges = setRemovedExceptionRanges;\n        }\n        else {\n          setCommonRemovedExceptionRanges.retainAll(setRemovedExceptionRanges);\n        }\n\n        // shift extern edges on split blocks\n        if (block.getSeq().isEmpty() && block.getSuccessors().size() == 1) {\n          BasicBlock successor = block.getSuccessors().get(0);\n          for (BasicBlock predecessor : new ArrayList<>(block.getPredecessors())) {\n            if (!setBlocks.contains(predecessor)) {\n              predecessor.replaceSuccessor(block, successor);\n            }\n          }\n\n          if (graph.getFirst() == block) {\n            graph.setFirst(successor);\n          }\n        }\n\n        graph.removeBlock(block);\n      }\n    }\n\n    if (isOutsideRange) {\n      // new empty block\n      BasicBlock emptyBlock = new BasicBlock(++graph.last_id);\n      graph.getBlocks().addWithKey(emptyBlock, emptyBlock.id);\n\n      // add to ranges if necessary\n      for (ExceptionRangeCFG range : setCommonRemovedExceptionRanges) {\n        emptyBlock.addSuccessorException(range.getHandler());\n        range.getProtectedRange().add(emptyBlock);\n      }\n\n      // insert between predecessors and next\n      emptyBlock.addSuccessor(next);\n      for (BasicBlock predecessor : setPredecessors) {\n        predecessor.replaceSuccessor(next, emptyBlock);\n      }\n    }\n  }\n\n  private static void removeExceptionInstructionsEx(BasicBlock block, int blockType, int finallyType) {\n    InstructionSequence seq = block.getSeq();\n\n    if (finallyType == 3) { // empty finally handler\n      for (int i = seq.length() - 1; i >= 0; i--) {\n        seq.removeInstruction(i);\n      }\n    }\n    else {\n      if ((blockType & 1) > 0) { // first\n        if (finallyType == 2 || finallyType == 1) { // `AStore` or `Pop`\n          seq.removeInstruction(0);\n        }\n      }\n\n      if ((blockType & 2) > 0) { // last\n        if (finallyType == 2 || finallyType == 0) {\n          seq.removeLast();\n        }\n\n        if (finallyType == 2) { // `AStore`\n          seq.removeLast();\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.attr.StructAnnotationAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructAnnotationParameterAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\n\nimport java.util.List;\n\npublic final class IdeaNotNullHelper {\n\n\n  public static boolean removeHardcodedChecks(Statement root, StructMethod mt) {\n\n    boolean checks_removed = false;\n\n    // parameter @NotNull annotations\n    while (findAndRemoveParameterCheck(root, mt)) { // iterate until nothing found. Each invocation removes one parameter check.\n      checks_removed = true;\n    }\n\n    // method @NotNull annotation\n    while (findAndRemoveReturnCheck(root, mt)) { // iterate until nothing found. Each invocation handles one method exit check.\n      checks_removed = true;\n    }\n\n    return checks_removed;\n  }\n\n  private static boolean findAndRemoveParameterCheck(Statement stat, StructMethod mt) {\n\n    Statement st = stat.getFirst();\n    while (st.type == StatementType.SEQUENCE) {\n      st = st.getFirst();\n    }\n\n    if (st.type == StatementType.IF) {\n\n      IfStatement ifstat = (IfStatement)st;\n      Statement ifbranch = ifstat.getIfstat();\n\n      Exprent if_condition = ifstat.getHeadexprent().getCondition();\n\n      boolean is_notnull_check = false;\n\n      // TODO: FUNCTION_NE also possible if reversed order (in theory)\n      if (ifbranch != null &&\n          if_condition.type == Exprent.EXPRENT_FUNCTION &&\n          ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ &&\n          ifbranch.type == StatementType.BASIC_BLOCK &&\n          ifbranch.getExprents().size() == 1 &&\n          ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {\n\n        FunctionExprent func = (FunctionExprent)if_condition;\n        Exprent first_param = func.getLstOperands().get(0);\n        Exprent second_param = func.getLstOperands().get(1);\n\n        if (second_param.type == Exprent.EXPRENT_CONST &&\n            second_param.getExprType().getType() == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order\n          if (first_param.type == Exprent.EXPRENT_VAR) {\n            VarExprent var = (VarExprent)first_param;\n\n            boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);\n\n            MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n\n            // parameter annotations\n            StructAnnotationParameterAttribute param_annotations =\n              mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS);\n            if (param_annotations != null) {\n\n              List<List<AnnotationExprent>> param_annotations_lists = param_annotations.getParamAnnotations();\n              int method_param_number = md.params.length;\n\n              int index = thisvar ? 1 : 0;\n              for (int i = 0; i < method_param_number; i++) {\n\n                if (index == var.getIndex()) {\n                  if (param_annotations_lists.size() >= method_param_number - i) {\n                    int shift = method_param_number -\n                                param_annotations_lists\n                                  .size(); // NOTE: workaround for compiler bug, count annotations starting with the last parameter\n\n                    List<AnnotationExprent> annotations = param_annotations_lists.get(i - shift);\n\n                    for (AnnotationExprent ann : annotations) {\n                      if (ann.getClassName().equals(\"org/jetbrains/annotations/NotNull\")) {\n                        is_notnull_check = true;\n                        break;\n                      }\n                    }\n                  }\n\n                  break;\n                }\n\n                index += md.params[i].getStackSize();\n              }\n            }\n          }\n        }\n      }\n\n      if (!is_notnull_check) {\n        return false;\n      }\n\n      removeParameterCheck(stat);\n\n      return true;\n    }\n\n    return false;\n  }\n\n  private static void removeParameterCheck(Statement stat) {\n\n    Statement st = stat.getFirst();\n    while (st.type == StatementType.SEQUENCE) {\n      st = st.getFirst();\n    }\n\n    IfStatement ifstat = (IfStatement)st;\n\n    if (ifstat.getElsestat() != null) { // if - else\n      StatEdge ifedge = ifstat.getIfEdge();\n      StatEdge elseedge = ifstat.getElseEdge();\n\n      Statement ifbranch = ifstat.getIfstat();\n      Statement elsebranch = ifstat.getElsestat();\n\n      ifstat.getFirst().removeSuccessor(ifedge);\n      ifstat.getFirst().removeSuccessor(elseedge);\n\n      ifstat.getStats().removeWithKey(ifbranch.id);\n      ifstat.getStats().removeWithKey(elsebranch.id);\n\n      if (!ifbranch.getAllSuccessorEdges().isEmpty()) {\n        ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0));\n      }\n\n      ifstat.getParent().replaceStatement(ifstat, elsebranch);\n      ifstat.getParent().setAllParent();\n    }\n  }\n\n  private static boolean findAndRemoveReturnCheck(Statement stat, StructMethod mt) {\n\n    boolean is_notnull_check = false;\n\n    // method annotation, refers to the return value\n    StructAnnotationAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS);\n    if (attr != null) {\n      List<AnnotationExprent> annotations = attr.getAnnotations();\n\n      for (AnnotationExprent ann : annotations) {\n        if (ann.getClassName().equals(\"org/jetbrains/annotations/NotNull\")) {\n          is_notnull_check = true;\n          break;\n        }\n      }\n    }\n\n    return is_notnull_check && removeReturnCheck(stat, mt);\n  }\n\n\n  private static boolean removeReturnCheck(Statement stat, StructMethod mt) {\n\n    Statement parent = stat.getParent();\n\n    if (parent != null && parent.type == StatementType.IF && stat.type == StatementType.BASIC_BLOCK && stat.getExprents().size() == 1) {\n      Exprent exprent = stat.getExprents().get(0);\n      if (exprent.type == Exprent.EXPRENT_EXIT) {\n        ExitExprent exit_exprent = (ExitExprent)exprent;\n        if (exit_exprent.getExitType() == ExitExprent.EXIT_RETURN) {\n          Exprent exprent_value = exit_exprent.getValue();\n          //if(exprent_value.type == Exprent.EXPRENT_VAR) {\n          //\tVarExprent var_value = (VarExprent)exprent_value;\n\n          IfStatement ifparent = (IfStatement)parent;\n          Exprent if_condition = ifparent.getHeadexprent().getCondition();\n\n          if (ifparent.getElsestat() == stat && if_condition.type == Exprent.EXPRENT_FUNCTION &&\n              ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory)\n\n            FunctionExprent func = (FunctionExprent)if_condition;\n            Exprent first_param = func.getLstOperands().get(0);\n            Exprent second_param = func.getLstOperands().get(1);\n\n            StatEdge ifedge = ifparent.getIfEdge();\n            StatEdge elseedge = ifparent.getElseEdge();\n\n            Statement ifbranch = ifparent.getIfstat();\n            Statement elsebranch = ifparent.getElsestat();\n\n            if (second_param.type == Exprent.EXPRENT_CONST &&\n                second_param.getExprType().getType() == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order\n              //if(first_param.type == Exprent.EXPRENT_VAR && ((VarExprent)first_param).getIndex() == var_value.getIndex()) {\n              if (first_param.equals(exprent_value)) {        // TODO: check for absence of side effects like method invocations etc.\n                if (ifbranch.type == StatementType.BASIC_BLOCK &&\n                    ifbranch.getExprents().size() == 1 &&\n                    // TODO: special check for IllegalStateException\n                    ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {\n\n                  ifparent.getFirst().removeSuccessor(ifedge);\n                  ifparent.getFirst().removeSuccessor(elseedge);\n\n                  ifparent.getStats().removeWithKey(ifbranch.id);\n                  ifparent.getStats().removeWithKey(elsebranch.id);\n\n                  if (!ifbranch.getAllSuccessorEdges().isEmpty()) {\n                    ifbranch.removeSuccessor(ifbranch.getAllSuccessorEdges().get(0));\n                  }\n\n                  if (!ifparent.getFirst().getExprents().isEmpty()) {\n                    elsebranch.getExprents().addAll(0, ifparent.getFirst().getExprents());\n                  }\n\n                  ifparent.getParent().replaceStatement(ifparent, elsebranch);\n                  ifparent.getParent().setAllParent();\n\n                  return true;\n                }\n              }\n            }\n          }\n          //}\n        }\n      }\n    }\n    else if (parent != null &&\n             parent.type == StatementType.SEQUENCE &&\n             stat.type == StatementType.BASIC_BLOCK &&\n             stat.getExprents().size() == 1) {\n      Exprent exprent = stat.getExprents().get(0);\n      if (exprent.type == Exprent.EXPRENT_EXIT) {\n        ExitExprent exit_exprent = (ExitExprent)exprent;\n        if (exit_exprent.getExitType() == ExitExprent.EXIT_RETURN) {\n          Exprent exprent_value = exit_exprent.getValue();\n\n          SequenceStatement sequence = (SequenceStatement)parent;\n          int sequence_stats_number = sequence.getStats().size();\n\n          if (sequence_stats_number > 1 &&\n              sequence.getStats().getLast() == stat &&\n              sequence.getStats().get(sequence_stats_number - 2).type == StatementType.IF) {\n\n            IfStatement ifstat = (IfStatement)sequence.getStats().get(sequence_stats_number - 2);\n            Exprent if_condition = ifstat.getHeadexprent().getCondition();\n\n            if (ifstat.iftype == IfStatement.IFTYPE_IF && if_condition.type == Exprent.EXPRENT_FUNCTION &&\n                ((FunctionExprent)if_condition).getFuncType() == FunctionExprent.FUNCTION_EQ) { // TODO: reversed order possible (in theory)\n\n              FunctionExprent func = (FunctionExprent)if_condition;\n              Exprent first_param = func.getLstOperands().get(0);\n              Exprent second_param = func.getLstOperands().get(1);\n\n              Statement ifbranch = ifstat.getIfstat();\n\n              if (second_param.type == Exprent.EXPRENT_CONST &&\n                  second_param.getExprType().getType() == CodeConstants.TYPE_NULL) { // TODO: reversed parameter order\n                if (first_param.equals(exprent_value)) {        // TODO: check for absence of side effects like method invocations etc.\n                  if (ifbranch.type == StatementType.BASIC_BLOCK &&\n                      ifbranch.getExprents().size() == 1 &&\n                      // TODO: special check for IllegalStateException\n                      ifbranch.getExprents().get(0).type == Exprent.EXPRENT_EXIT) {\n\n                    ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0)); // remove 'else' edge\n\n                    if (!ifstat.getFirst().getExprents().isEmpty()) {\n                      stat.getExprents().addAll(0, ifstat.getFirst().getExprents());\n                    }\n\n                    for (StatEdge edge : ifstat.getAllPredecessorEdges()) {\n\n                      ifstat.removePredecessor(edge);\n                      edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, stat);\n                      stat.addPredecessor(edge);\n                    }\n\n                    sequence.getStats().removeWithKey(ifstat.id);\n                    sequence.setFirst(sequence.getStats().get(0));\n\n                    return true;\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n\n\n    for (Statement st : stat.getStats()) {\n      if (removeReturnCheck(st, mt)) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.*;\n\npublic final class IfHelper {\n  public static boolean mergeAllIfs(RootStatement root) {\n    boolean res = mergeAllIfsRec(root, new HashSet<>());\n    if (res) {\n      SequenceHelper.condenseSequences(root);\n    }\n    return res;\n  }\n\n  private static boolean mergeAllIfsRec(Statement stat, Set<? super Integer> setReorderedIfs) {\n    boolean res = false;\n\n    if (stat.getExprents() == null) {\n      while (true) {\n        boolean changed = false;\n\n        for (Statement st : stat.getStats()) {\n          res |= mergeAllIfsRec(st, setReorderedIfs);\n\n          // collapse composed if's\n          if (mergeIfs(st, setReorderedIfs)) {\n            changed = true;\n            break;\n          }\n        }\n\n        res |= changed;\n\n        if (!changed) {\n          break;\n        }\n      }\n    }\n\n    return res;\n  }\n\n  public static boolean mergeIfs(Statement statement, Set<? super Integer> setReorderedIfs) {\n    if (statement.type != StatementType.IF && statement.type != StatementType.SEQUENCE) {\n      return false;\n    }\n\n    boolean res = false;\n\n    while (true) {\n      boolean updated = false;\n\n      List<Statement> lst = new ArrayList<>();\n      if (statement.type == StatementType.IF) {\n        lst.add(statement);\n      }\n      else {\n        lst.addAll(statement.getStats());\n      }\n\n      boolean stsingle = (lst.size() == 1);\n\n      for (Statement stat : lst) {\n        if (stat.type == StatementType.IF) {\n          IfNode rtnode = buildGraph((IfStatement)stat, stsingle);\n\n          if (rtnode == null) {\n            continue;\n          }\n\n          if (collapseIfIf(rtnode)) {\n            updated = true;\n            break;\n          }\n\n          if (!setReorderedIfs.contains(stat.id)) {\n            if (collapseIfElse(rtnode)) {\n              updated = true;\n              break;\n            }\n\n            if (collapseElse(rtnode)) {\n              updated = true;\n              break;\n            }\n          }\n\n          if (reorderIf((IfStatement)stat)) {\n            updated = true;\n            setReorderedIfs.add(stat.id);\n            break;\n          }\n        }\n      }\n\n      if (!updated) {\n        break;\n      }\n\n      res = true;\n    }\n\n    return res;\n  }\n\n  private static boolean collapseIfIf(IfNode rtnode) {\n    if (rtnode.edgetypes.get(0) == 0) {\n      IfNode ifbranch = rtnode.succs.get(0);\n      if (ifbranch.succs.size() == 2) {\n\n        // if-if branch\n        if (ifbranch.succs.get(1).value == rtnode.succs.get(1).value) {\n\n          IfStatement ifparent = (IfStatement)rtnode.value;\n          IfStatement ifchild = (IfStatement)ifbranch.value;\n          Statement ifinner = ifbranch.succs.get(0).value;\n\n          if (ifchild.getFirst().getExprents().isEmpty()) {\n\n            ifparent.getFirst().removeSuccessor(ifparent.getIfEdge());\n            ifchild.removeSuccessor(ifchild.getAllSuccessorEdges().get(0));\n            ifparent.getStats().removeWithKey(ifchild.id);\n\n            if (ifbranch.edgetypes.get(0) == 1) { // target null\n\n              ifparent.setIfstat(null);\n\n              StatEdge ifedge = ifchild.getIfEdge();\n\n              ifchild.getFirst().removeSuccessor(ifedge);\n              ifedge.setSource(ifparent.getFirst());\n\n              if (ifedge.closure == ifchild) {\n                ifedge.closure = null;\n              }\n              ifparent.getFirst().addSuccessor(ifedge);\n\n              ifparent.setIfEdge(ifedge);\n            }\n            else {\n              ifchild.getFirst().removeSuccessor(ifchild.getIfEdge());\n\n              StatEdge ifedge = new StatEdge(EdgeType.REGULAR, ifparent.getFirst(), ifinner);\n              ifparent.getFirst().addSuccessor(ifedge);\n              ifparent.setIfEdge(ifedge);\n              ifparent.setIfstat(ifinner);\n\n              ifparent.getStats().addWithKey(ifinner, ifinner.id);\n              ifinner.setParent(ifparent);\n\n              if (!ifinner.getAllSuccessorEdges().isEmpty()) {\n                StatEdge edge = ifinner.getAllSuccessorEdges().get(0);\n                if (edge.closure == ifchild) {\n                  edge.closure = null;\n                }\n              }\n            }\n\n            // merge if conditions\n            IfExprent statexpr = ifparent.getHeadexprent();\n\n            List<Exprent> lstOperands = new ArrayList<>();\n            lstOperands.add(statexpr.getCondition());\n            lstOperands.add(ifchild.getHeadexprent().getCondition());\n\n            statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands, null));\n            statexpr.addBytecodeOffsets(ifchild.getHeadexprent().bytecode);\n\n            return true;\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static boolean collapseIfElse(IfNode rtnode) {\n    if (rtnode.edgetypes.get(0) == 0) {\n      IfNode ifbranch = rtnode.succs.get(0);\n      if (ifbranch.succs.size() == 2) {\n        // if-else branch\n        if (ifbranch.succs.get(0).value == rtnode.succs.get(1).value) {\n\n          IfStatement ifparent = (IfStatement)rtnode.value;\n          IfStatement ifchild = (IfStatement)ifbranch.value;\n\n          if (ifchild.getFirst().getExprents().isEmpty()) {\n\n            ifparent.getFirst().removeSuccessor(ifparent.getIfEdge());\n            ifchild.getFirst().removeSuccessor(ifchild.getIfEdge());\n            ifparent.getStats().removeWithKey(ifchild.id);\n\n            if (ifbranch.edgetypes.get(1) == 1 &&\n                ifbranch.edgetypes.get(0) == 1) { // target null\n\n              ifparent.setIfstat(null);\n\n              StatEdge ifedge = ifchild.getAllSuccessorEdges().get(0);\n\n              ifchild.removeSuccessor(ifedge);\n              ifedge.setSource(ifparent.getFirst());\n              ifparent.getFirst().addSuccessor(ifedge);\n\n              ifparent.setIfEdge(ifedge);\n            }\n            else {\n              throw new RuntimeException(\"inconsistent if structure!\");\n            }\n\n            // merge if conditions\n            IfExprent statexpr = ifparent.getHeadexprent();\n\n            List<Exprent> lstOperands = new ArrayList<>();\n            lstOperands.add(statexpr.getCondition());\n            lstOperands.add(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, ifchild.getHeadexprent().getCondition(), null));\n            statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_CADD, lstOperands, null));\n            statexpr.addBytecodeOffsets(ifchild.getHeadexprent().bytecode);\n\n            return true;\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static boolean collapseElse(IfNode rtnode) {\n    if (rtnode.edgetypes.get(1) == 0) {\n      IfNode elsebranch = rtnode.succs.get(1);\n      if (elsebranch.succs.size() == 2) {\n\n        // else-if or else-else branch\n        int path = elsebranch.succs.get(1).value == rtnode.succs.get(0).value ? 2 :\n                   (elsebranch.succs.get(0).value == rtnode.succs.get(0).value ? 1 : 0);\n\n        if (path > 0) {\n\n          IfStatement firstif = (IfStatement)rtnode.value;\n          IfStatement secondif = (IfStatement)elsebranch.value;\n          Statement parent = firstif.getParent();\n\n          if (secondif.getFirst().getExprents().isEmpty()) {\n\n            firstif.getFirst().removeSuccessor(firstif.getIfEdge());\n\n            // remove first if\n            firstif.removeAllSuccessors(secondif);\n\n            for (StatEdge edge : firstif.getAllPredecessorEdges()) {\n              if (!firstif.containsStatementStrict(edge.getSource())) {\n                firstif.removePredecessor(edge);\n                edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, secondif);\n                secondif.addPredecessor(edge);\n              }\n            }\n\n            parent.getStats().removeWithKey(firstif.id);\n            if (parent.getFirst() == firstif) {\n              parent.setFirst(secondif);\n            }\n\n            // merge if conditions\n            IfExprent statexpr = secondif.getHeadexprent();\n\n            List<Exprent> lstOperands = new ArrayList<>();\n            lstOperands.add(firstif.getHeadexprent().getCondition());\n\n            if (path == 2) {\n              lstOperands.set(0, new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, lstOperands.get(0), null));\n            }\n\n            lstOperands.add(statexpr.getCondition());\n\n            statexpr\n              .setCondition(new FunctionExprent(path == 1 ? FunctionExprent.FUNCTION_COR : FunctionExprent.FUNCTION_CADD, lstOperands, null));\n\n            if (secondif.getFirst().getExprents().isEmpty() &&\n                !firstif.getFirst().getExprents().isEmpty()) {\n\n              secondif.replaceStatement(secondif.getFirst(), firstif.getFirst());\n            }\n\n            return true;\n          }\n        }\n      }\n      else if (elsebranch.succs.size() == 1) {\n        if (elsebranch.succs.get(0).value == rtnode.succs.get(0).value) {\n          IfStatement firstif = (IfStatement)rtnode.value;\n          Statement second = elsebranch.value;\n\n          firstif.removeAllSuccessors(second);\n\n          for (StatEdge edge : second.getAllSuccessorEdges()) {\n            second.removeSuccessor(edge);\n            edge.setSource(firstif);\n            firstif.addSuccessor(edge);\n          }\n\n          StatEdge ifedge = firstif.getIfEdge();\n          firstif.getFirst().removeSuccessor(ifedge);\n\n          second.addSuccessor(new StatEdge(ifedge.getType(), second, ifedge.getDestination(), ifedge.closure));\n\n          StatEdge newifedge = new StatEdge(EdgeType.REGULAR, firstif.getFirst(), second);\n          firstif.getFirst().addSuccessor(newifedge);\n          firstif.setIfstat(second);\n\n          firstif.getStats().addWithKey(second, second.id);\n          second.setParent(firstif);\n\n          firstif.getParent().getStats().removeWithKey(second.id);\n\n          // negate the if condition\n          IfExprent statexpr = firstif.getHeadexprent();\n          statexpr\n            .setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, statexpr.getCondition(), null));\n\n          return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static IfNode buildGraph(IfStatement stat, boolean stsingle) {\n    if (stat.iftype == IfStatement.IFTYPE_IFELSE) {\n      return null;\n    }\n\n    IfNode res = new IfNode(stat);\n\n    // if branch\n    Statement ifchild = stat.getIfstat();\n    if (ifchild == null) {\n      StatEdge edge = stat.getIfEdge();\n      res.addChild(new IfNode(edge.getDestination()), 1);\n    }\n    else {\n      IfNode ifnode = new IfNode(ifchild);\n      res.addChild(ifnode, 0);\n      if (ifchild.type == StatementType.IF && ((IfStatement)ifchild).iftype == IfStatement.IFTYPE_IF) {\n        IfStatement stat2 = (IfStatement)ifchild;\n        Statement ifchild2 = stat2.getIfstat();\n        if (ifchild2 == null) {\n          StatEdge edge = stat2.getIfEdge();\n          ifnode.addChild(new IfNode(edge.getDestination()), 1);\n        }\n        else {\n          ifnode.addChild(new IfNode(ifchild2), 0);\n        }\n      }\n\n      if (!ifchild.getAllSuccessorEdges().isEmpty()) {\n        ifnode.addChild(new IfNode(ifchild.getAllSuccessorEdges().get(0).getDestination()), 1);\n      }\n    }\n\n    // else branch\n    StatEdge edge = stat.getAllSuccessorEdges().get(0);\n    Statement elsechild = edge.getDestination();\n    IfNode elsenode = new IfNode(elsechild);\n\n    if (stsingle || edge.getType() != EdgeType.REGULAR) {\n      res.addChild(elsenode, 1);\n    }\n    else {\n      res.addChild(elsenode, 0);\n      if (elsechild.type == StatementType.IF && ((IfStatement)elsechild).iftype == IfStatement.IFTYPE_IF) {\n        IfStatement stat2 = (IfStatement)elsechild;\n        Statement ifchild2 = stat2.getIfstat();\n        if (ifchild2 == null) {\n          elsenode.addChild(new IfNode(stat2.getIfEdge().getDestination()), 1);\n        }\n        else {\n          elsenode.addChild(new IfNode(ifchild2), 0);\n        }\n      }\n\n      if (!elsechild.getAllSuccessorEdges().isEmpty()) {\n        elsenode.addChild(new IfNode(elsechild.getAllSuccessorEdges().get(0).getDestination()), 1);\n      }\n    }\n\n    return res;\n  }\n\n  // FIXME: rewrite the entire method!!! keep in mind finally exits!!\n  private static boolean reorderIf(IfStatement ifstat) {\n    if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) {\n      return false;\n    }\n\n    boolean ifdirect, elsedirect;\n    boolean noifstat = false, noelsestat;\n    boolean ifdirectpath = false, elsedirectpath = false;\n\n    Statement parent = ifstat.getParent();\n    Statement from = parent.type == StatementType.SEQUENCE ? parent : ifstat;\n\n    Statement next = getNextStatement(from);\n\n    if (ifstat.getIfstat() == null) {\n      noifstat = true;\n\n      ifdirect = ifstat.getIfEdge().getType() == EdgeType.FINALLY_EXIT ||\n                 MergeHelper.isDirectPath(from, ifstat.getIfEdge().getDestination());\n    }\n    else {\n      List<StatEdge> lstSuccs = ifstat.getIfstat().getAllSuccessorEdges();\n      ifdirect = !lstSuccs.isEmpty() && lstSuccs.get(0).getType() == EdgeType.FINALLY_EXIT ||\n                 hasDirectEndEdge(ifstat.getIfstat(), from);\n    }\n\n    Statement last = parent.type == StatementType.SEQUENCE ? parent.getStats().getLast() : ifstat;\n    noelsestat = (last == ifstat);\n\n    elsedirect = !last.getAllSuccessorEdges().isEmpty() && last.getAllSuccessorEdges().get(0).getType() == EdgeType.FINALLY_EXIT ||\n                 hasDirectEndEdge(last, from);\n\n    if (!noelsestat && existsPath(ifstat, ifstat.getAllSuccessorEdges().get(0).getDestination())) {\n      return false;\n    }\n\n    if (!ifdirect && !noifstat) {\n      ifdirectpath = existsPath(ifstat, next);\n    }\n\n    if (!elsedirect && !noelsestat) {\n      SequenceStatement sequence = (SequenceStatement)parent;\n\n      for (int i = sequence.getStats().size() - 1; i >= 0; i--) {\n        Statement sttemp = sequence.getStats().get(i);\n        if (sttemp == ifstat) {\n          break;\n        }\n        else if (existsPath(sttemp, next)) {\n          elsedirectpath = true;\n          break;\n        }\n      }\n    }\n\n    if ((ifdirect || ifdirectpath) && (elsedirect || elsedirectpath) && !noifstat && !noelsestat) {  // if - then - else\n\n      SequenceStatement sequence = (SequenceStatement)parent;\n\n      // build and cut the new else statement\n      List<Statement> lst = new ArrayList<>();\n      for (int i = sequence.getStats().size() - 1; i >= 0; i--) {\n        Statement sttemp = sequence.getStats().get(i);\n        if (sttemp == ifstat) {\n          break;\n        }\n        else {\n          lst.add(0, sttemp);\n        }\n      }\n\n      Statement stelse;\n      if (lst.size() == 1) {\n        stelse = lst.get(0);\n      }\n      else {\n        stelse = new SequenceStatement(lst);\n        stelse.setAllParent();\n      }\n\n      ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0));\n      for (Statement st : lst) {\n        sequence.getStats().removeWithKey(st.id);\n      }\n\n      StatEdge elseedge = new StatEdge(EdgeType.REGULAR, ifstat.getFirst(), stelse);\n      ifstat.getFirst().addSuccessor(elseedge);\n      ifstat.setElsestat(stelse);\n      ifstat.setElseEdge(elseedge);\n\n      ifstat.getStats().addWithKey(stelse, stelse.id);\n      stelse.setParent(ifstat);\n\n      //\t\t\tif(next.type != Statement.TYPE_DUMMYEXIT && (ifdirect || elsedirect)) {\n      //\t \t\t\tStatEdge breakedge = new StatEdge(StatEdge.TYPE_BREAK, ifstat, next);\n      //\t\t\t\tsequence.addLabeledEdge(breakedge);\n      //\t\t\t\tifstat.addSuccessor(breakedge);\n      //\t\t\t}\n\n      ifstat.iftype = IfStatement.IFTYPE_IFELSE;\n    }\n    else if (ifdirect && (!elsedirect || (noifstat && !noelsestat))) {  // if - then\n\n      // negate the if condition\n      IfExprent statexpr = ifstat.getHeadexprent();\n      statexpr.setCondition(new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, statexpr.getCondition(), null));\n\n      if (noelsestat) {\n        StatEdge ifedge = ifstat.getIfEdge();\n        StatEdge elseedge = ifstat.getAllSuccessorEdges().get(0);\n\n        if (noifstat) {\n          ifstat.getFirst().removeSuccessor(ifedge);\n          ifstat.removeSuccessor(elseedge);\n\n          ifedge.setSource(ifstat);\n          elseedge.setSource(ifstat.getFirst());\n\n          ifstat.addSuccessor(ifedge);\n          ifstat.getFirst().addSuccessor(elseedge);\n\n          ifstat.setIfEdge(elseedge);\n        }\n        else {\n          Statement ifbranch = ifstat.getIfstat();\n          SequenceStatement newseq = new SequenceStatement(Arrays.asList(ifstat, ifbranch));\n\n          ifstat.getFirst().removeSuccessor(ifedge);\n          ifstat.getStats().removeWithKey(ifbranch.id);\n          ifstat.setIfstat(null);\n\n          ifstat.removeSuccessor(elseedge);\n          elseedge.setSource(ifstat.getFirst());\n          ifstat.getFirst().addSuccessor(elseedge);\n\n          ifstat.setIfEdge(elseedge);\n\n          ifstat.getParent().replaceStatement(ifstat, newseq);\n          newseq.setAllParent();\n\n          ifstat.addSuccessor(new StatEdge(EdgeType.REGULAR, ifstat, ifbranch));\n        }\n      }\n      else {\n\n        SequenceStatement sequence = (SequenceStatement)parent;\n\n        // build and cut the new else statement\n        List<Statement> lst = new ArrayList<>();\n        for (int i = sequence.getStats().size() - 1; i >= 0; i--) {\n          Statement sttemp = sequence.getStats().get(i);\n          if (sttemp == ifstat) {\n            break;\n          }\n          else {\n            lst.add(0, sttemp);\n          }\n        }\n\n        Statement stelse;\n        if (lst.size() == 1) {\n          stelse = lst.get(0);\n        }\n        else {\n          stelse = new SequenceStatement(lst);\n          stelse.setAllParent();\n        }\n\n        ifstat.removeSuccessor(ifstat.getAllSuccessorEdges().get(0));\n        for (Statement st : lst) {\n          sequence.getStats().removeWithKey(st.id);\n        }\n\n        if (noifstat) {\n          StatEdge ifedge = ifstat.getIfEdge();\n\n          ifstat.getFirst().removeSuccessor(ifedge);\n          ifedge.setSource(ifstat);\n          ifstat.addSuccessor(ifedge);\n        }\n        else {\n          Statement ifbranch = ifstat.getIfstat();\n\n          ifstat.getFirst().removeSuccessor(ifstat.getIfEdge());\n          ifstat.getStats().removeWithKey(ifbranch.id);\n\n          ifstat.addSuccessor(new StatEdge(EdgeType.REGULAR, ifstat, ifbranch));\n\n          sequence.getStats().addWithKey(ifbranch, ifbranch.id);\n          ifbranch.setParent(sequence);\n        }\n\n        StatEdge newifedge = new StatEdge(EdgeType.REGULAR, ifstat.getFirst(), stelse);\n        ifstat.getFirst().addSuccessor(newifedge);\n        ifstat.setIfstat(stelse);\n        ifstat.setIfEdge(newifedge);\n\n        ifstat.getStats().addWithKey(stelse, stelse.id);\n        stelse.setParent(ifstat);\n      }\n    }\n    else {\n      return false;\n    }\n\n    return true;\n  }\n\n  private static boolean hasDirectEndEdge(Statement stat, Statement from) {\n\n    for (StatEdge edge : stat.getAllSuccessorEdges()) {\n      if (MergeHelper.isDirectPath(from, edge.getDestination())) {\n        return true;\n      }\n    }\n\n    if (stat.getExprents() == null) {\n      switch (stat.type) {\n        case SEQUENCE -> {\n          return hasDirectEndEdge(stat.getStats().getLast(), from);\n        }\n        case CATCH_ALL, TRY_CATCH -> {\n          for (Statement st : stat.getStats()) {\n            if (hasDirectEndEdge(st, from)) {\n              return true;\n            }\n          }\n        }\n        case IF -> {\n          IfStatement ifstat = (IfStatement)stat;\n          if (ifstat.iftype == IfStatement.IFTYPE_IFELSE) {\n            return hasDirectEndEdge(ifstat.getIfstat(), from) ||\n                   hasDirectEndEdge(ifstat.getElsestat(), from);\n          }\n        }\n        case SYNCHRONIZED -> {\n          return hasDirectEndEdge(stat.getStats().get(1), from);\n        }\n        case SWITCH -> {\n          for (Statement st : stat.getStats()) {\n            if (hasDirectEndEdge(st, from)) {\n              return true;\n            }\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static Statement getNextStatement(Statement stat) {\n    Statement parent = stat.getParent();\n    switch (parent.type) {\n      case ROOT -> {\n        return ((RootStatement)parent).getDummyExit();\n      }\n      case DO -> {\n        return parent;\n      }\n      case SEQUENCE -> {\n        SequenceStatement sequence = (SequenceStatement)parent;\n        if (sequence.getStats().getLast() != stat) {\n          for (int i = sequence.getStats().size() - 1; i >= 0; i--) {\n            if (sequence.getStats().get(i) == stat) {\n              return sequence.getStats().get(i + 1);\n            }\n          }\n        }\n      }\n    }\n\n    return getNextStatement(parent);\n  }\n\n  private static boolean existsPath(Statement from, Statement to) {\n    for (StatEdge edge : to.getAllPredecessorEdges()) {\n      if (from.containsStatementStrict(edge.getSource())) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  private static class IfNode {\n    public final Statement value;\n    public final List<IfNode> succs = new ArrayList<>();\n    public final List<Integer> edgetypes = new ArrayList<>();\n\n    IfNode(Statement value) {\n      this.value = value;\n    }\n\n    public void addChild(IfNode child, int type) {\n      succs.add(child);\n      edgetypes.add(type);\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\npublic final class InlineSingleBlockHelper {\n\n\n  public static boolean inlineSingleBlocks(RootStatement root) {\n\n    boolean res = inlineSingleBlocksRec(root);\n\n    if (res) {\n      SequenceHelper.condenseSequences(root);\n    }\n\n    return res;\n  }\n\n  private static boolean inlineSingleBlocksRec(Statement stat) {\n\n    boolean res = false;\n\n    for (Statement st : stat.getStats()) {\n      res |= inlineSingleBlocksRec(st);\n    }\n\n    if (stat.type == StatementType.SEQUENCE) {\n\n      SequenceStatement seq = (SequenceStatement)stat;\n      for (int i = 1; i < seq.getStats().size(); i++) {\n        if (isInlineable(seq, i)) {\n          inlineBlock(seq, i);\n          return true;\n        }\n      }\n    }\n\n    return res;\n  }\n\n  private static void inlineBlock(SequenceStatement seq, int index) {\n\n    Statement first = seq.getStats().get(index);\n    Statement pre = seq.getStats().get(index - 1);\n    pre.removeSuccessor(pre.getAllSuccessorEdges().get(0));   // single regular edge\n\n    StatEdge edge = first.getPredecessorEdges(EdgeType.BREAK).get(0);\n    Statement source = edge.getSource();\n    Statement parent = source.getParent();\n    source.removeSuccessor(edge);\n\n    List<Statement> lst = new ArrayList<>();\n    for (int i = seq.getStats().size() - 1; i >= index; i--) {\n      lst.add(0, seq.getStats().remove(i));\n    }\n\n    if (parent.type == StatementType.IF && ((IfStatement)parent).iftype == IfStatement.IFTYPE_IF &&\n        source == parent.getFirst()) {\n      IfStatement ifparent = (IfStatement)parent;\n      SequenceStatement block = new SequenceStatement(lst);\n      block.setAllParent();\n\n      StatEdge newedge = new StatEdge(EdgeType.REGULAR, source, block);\n      source.addSuccessor(newedge);\n      ifparent.setIfEdge(newedge);\n      ifparent.setIfstat(block);\n\n      ifparent.getStats().addWithKey(block, block.id);\n      block.setParent(ifparent);\n    }\n    else {\n      lst.add(0, source);\n\n      SequenceStatement block = new SequenceStatement(lst);\n      block.setAllParent();\n\n      parent.replaceStatement(source, block);\n\n      // LabelHelper.lowContinueLabels not applicable because of forward continue edges\n      // LabelHelper.lowContinueLabels(block, new HashSet<StatEdge>());\n      // do it by hand\n      for (StatEdge prededge : block.getPredecessorEdges(EdgeType.CONTINUE)) {\n\n        block.removePredecessor(prededge);\n        prededge.getSource().changeEdgeNode(EdgeDirection.FORWARD, prededge, source);\n        source.addPredecessor(prededge);\n\n        source.addLabeledEdge(prededge);\n      }\n\n\n      if (parent.type == StatementType.SWITCH) {\n        ((SwitchStatement)parent).sortEdgesAndNodes();\n      }\n\n      source.addSuccessor(new StatEdge(EdgeType.REGULAR, source, first));\n    }\n  }\n\n  private static boolean isInlineable(SequenceStatement seq, int index) {\n\n    Statement first = seq.getStats().get(index);\n    Statement pre = seq.getStats().get(index - 1);\n\n    if (pre.hasBasicSuccEdge()) {\n      return false;\n    }\n\n\n    List<StatEdge> lst = first.getPredecessorEdges(EdgeType.BREAK);\n\n    if (lst.size() == 1) {\n      StatEdge edge = lst.get(0);\n\n      if (sameCatchRanges(edge)) {\n        if (!edge.explicit) {\n          for (int i = index; i < seq.getStats().size(); i++) {\n            if (!noExitLabels(seq.getStats().get(i), seq)) {\n              return false;\n            }\n          }\n        }\n        return true;\n      }\n      // FIXME: count labels properly\n    }\n\n    return false;\n  }\n\n  private static boolean sameCatchRanges(StatEdge edge) {\n\n    Statement from = edge.getSource();\n    Statement to = edge.getDestination();\n\n    while (true) {\n\n      Statement parent = from.getParent();\n      if (parent.containsStatementStrict(to)) {\n        break;\n      }\n\n      if (parent.type == StatementType.TRY_CATCH ||\n          parent.type == StatementType.CATCH_ALL) {\n        if (parent.getFirst() == from) {\n          return false;\n        }\n      }\n      else if (parent.type == StatementType.SYNCHRONIZED) {\n        if (parent.getStats().get(1) == from) {\n          return false;\n        }\n      }\n\n      from = parent;\n    }\n\n    return true;\n  }\n\n  private static boolean noExitLabels(Statement block, Statement sequence) {\n\n    for (StatEdge edge : block.getAllSuccessorEdges()) {\n      if (edge.getType() != EdgeType.REGULAR && edge.getDestination().type != StatementType.DUMMY_EXIT) {\n        if (!sequence.containsStatementStrict(edge.getDestination())) {\n          return false;\n        }\n      }\n    }\n\n    for (Statement st : block.getStats()) {\n      if (!noExitLabels(st, sequence)) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  public static boolean isBreakEdgeLabeled(Statement source, Statement closure) {\n    if (closure.type == StatementType.DO || closure.type == StatementType.SWITCH) {\n      Statement parent = source.getParent();\n      return parent != closure &&\n             (parent.type == StatementType.DO || parent.type == StatementType.SWITCH || isBreakEdgeLabeled(parent, closure));\n    }\n    else {\n      return true;\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\n\npublic final class LabelHelper {\n\n\n  public static void cleanUpEdges(RootStatement root) {\n\n    resetAllEdges(root);\n\n    removeNonImmediateEdges(root);\n\n    liftClosures(root);\n\n    lowContinueLabels(root, new HashSet<>());\n\n    lowClosures(root);\n  }\n\n  public static void identifyLabels(RootStatement root) {\n\n    setExplicitEdges(root);\n\n    hideDefaultSwitchEdges(root);\n\n    processStatementLabel(root);\n\n    setRetEdgesUnlabeled(root);\n  }\n\n  private static void liftClosures(Statement stat) {\n\n    for (StatEdge edge : stat.getAllSuccessorEdges()) {\n      if (EdgeType.CONTINUE.equals(edge.getType())) {\n        if (edge.getDestination() != edge.closure) {\n          edge.getDestination().addLabeledEdge(edge);\n        }\n      }\n      else if (EdgeType.BREAK.equals(edge.getType())) {\n        Statement dest = edge.getDestination();\n        if (dest.type != StatementType.DUMMY_EXIT) {\n          Statement parent = dest.getParent();\n\n          List<Statement> lst = new ArrayList<>();\n          if (parent.type == StatementType.SEQUENCE) {\n            lst.addAll(parent.getStats());\n          }\n          else if (parent.type == StatementType.SWITCH) {\n            lst.addAll(((SwitchStatement)parent).getCaseStatements());\n          }\n\n          for (int i = 0; i < lst.size(); i++) {\n            if (lst.get(i) == dest) {\n              lst.get(i - 1).addLabeledEdge(edge);\n              break;\n            }\n          }\n        }\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      liftClosures(st);\n    }\n  }\n\n  private static void removeNonImmediateEdges(Statement stat) {\n\n    for (Statement st : stat.getStats()) {\n      removeNonImmediateEdges(st);\n    }\n\n    if (!stat.hasBasicSuccEdge()) {\n      for (StatEdge edge : stat.getSuccessorEdges(EdgeType.CONTINUE.unite(EdgeType.BREAK))) {\n        stat.removeSuccessor(edge);\n      }\n    }\n  }\n\n  public static void lowContinueLabels(Statement stat, HashSet<StatEdge> edges) {\n\n    boolean ok = (stat.type != StatementType.DO);\n    if (!ok) {\n      DoStatement dostat = (DoStatement)stat;\n      ok = dostat.getLoopType() == LoopType.DO ||\n           dostat.getLoopType() == LoopType.WHILE ||\n           (dostat.getLoopType() == LoopType.FOR && dostat.getIncExprent() == null);\n    }\n\n    if (ok) {\n      edges.addAll(stat.getPredecessorEdges(EdgeType.CONTINUE));\n    }\n\n    if (ok && stat.type == StatementType.DO) {\n      for (StatEdge edge : edges) {\n        if (stat.containsStatementStrict(edge.getSource())) {\n\n          edge.getDestination().removePredecessor(edge);\n          edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, stat);\n          stat.addPredecessor(edge);\n\n          stat.addLabeledEdge(edge);\n        }\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      if (st == stat.getFirst()) {\n        lowContinueLabels(st, edges);\n      }\n      else {\n        lowContinueLabels(st, new HashSet<>());\n      }\n    }\n  }\n\n  public static void lowClosures(Statement stat) {\n\n    for (StatEdge edge : new ArrayList<>(stat.getLabelEdges())) {\n\n      if (edge.getType() == EdgeType.BREAK) {  // FIXME: ?\n        for (Statement st : stat.getStats()) {\n          if (st.containsStatementStrict(edge.getSource())) {\n            if (MergeHelper.isDirectPath(st, edge.getDestination())) {\n              st.addLabeledEdge(edge);\n            }\n          }\n        }\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      lowClosures(st);\n    }\n  }\n\n  private static void resetAllEdges(Statement stat) {\n\n    for (Statement st : stat.getStats()) {\n      resetAllEdges(st);\n    }\n\n    for (StatEdge edge : stat.getAllSuccessorEdges()) {\n      edge.explicit = true;\n      edge.labeled = true;\n    }\n  }\n\n  private static void setRetEdgesUnlabeled(RootStatement root) {\n    Statement exit = root.getDummyExit();\n    for (StatEdge edge : exit.getAllPredecessorEdges()) {\n      List<Exprent> lst = edge.getSource().getExprents();\n      if (edge.getType() == EdgeType.FINALLY_EXIT || (lst != null && !lst.isEmpty() &&\n                                                          lst.get(lst.size() - 1).type == Exprent.EXPRENT_EXIT)) {\n        edge.labeled = false;\n      }\n    }\n  }\n\n  private static HashMap<Statement, List<StatEdge>> setExplicitEdges(Statement stat) {\n\n    HashMap<Statement, List<StatEdge>> mapEdges = new HashMap<>();\n\n    if (stat.getExprents() != null) {\n      return mapEdges;\n    }\n\n\n    switch (stat.type) {\n      case TRY_CATCH, CATCH_ALL -> {\n        for (Statement st : stat.getStats()) {\n          HashMap<Statement, List<StatEdge>> mapEdges1 = setExplicitEdges(st);\n          processEdgesWithNext(st, mapEdges1, null);\n\n          if (stat.type == StatementType.TRY_CATCH || st == stat.getFirst()) { // edges leaving a finally catch block are always explicit\n            // merge the maps\n            if (mapEdges1 != null) {\n              for (Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) {\n                if (mapEdges.containsKey(entr.getKey())) {\n                  mapEdges.get(entr.getKey()).addAll(entr.getValue());\n                }\n                else {\n                  mapEdges.put(entr.getKey(), entr.getValue());\n                }\n              }\n            }\n          }\n        }\n      }\n      case DO -> {\n        mapEdges = setExplicitEdges(stat.getFirst());\n        processEdgesWithNext(stat.getFirst(), mapEdges, stat);\n      }\n      case IF -> {\n        IfStatement ifstat = (IfStatement)stat;\n        // head statement is a basic block\n        if (ifstat.getIfstat() == null) { // empty if\n          processEdgesWithNext(ifstat.getFirst(), mapEdges, null);\n        }\n        else {\n          mapEdges = setExplicitEdges(ifstat.getIfstat());\n          processEdgesWithNext(ifstat.getIfstat(), mapEdges, null);\n\n          HashMap<Statement, List<StatEdge>> mapEdges1 = null;\n          if (ifstat.getElsestat() != null) {\n            mapEdges1 = setExplicitEdges(ifstat.getElsestat());\n            processEdgesWithNext(ifstat.getElsestat(), mapEdges1, null);\n          }\n\n          // merge the maps\n          if (mapEdges1 != null) {\n            for (Entry<Statement, List<StatEdge>> entr : mapEdges1.entrySet()) {\n              if (mapEdges.containsKey(entr.getKey())) {\n                mapEdges.get(entr.getKey()).addAll(entr.getValue());\n              }\n              else {\n                mapEdges.put(entr.getKey(), entr.getValue());\n              }\n            }\n          }\n        }\n      }\n      case ROOT -> {\n        mapEdges = setExplicitEdges(stat.getFirst());\n        processEdgesWithNext(stat.getFirst(), mapEdges, ((RootStatement)stat).getDummyExit());\n      }\n      case SEQUENCE -> {\n        int index = 0;\n        while (index < stat.getStats().size() - 1) {\n          Statement st = stat.getStats().get(index);\n          processEdgesWithNext(st, setExplicitEdges(st), stat.getStats().get(index + 1));\n          index++;\n        }\n\n        Statement st = stat.getStats().get(index);\n        mapEdges = setExplicitEdges(st);\n        processEdgesWithNext(st, mapEdges, null);\n      }\n      case SWITCH -> {\n        SwitchStatement swst = (SwitchStatement)stat;\n\n        for (int i = 0; i < swst.getCaseStatements().size() - 1; i++) {\n          Statement stt = swst.getCaseStatements().get(i);\n          Statement stnext = swst.getCaseStatements().get(i + 1);\n\n          if (stnext.getExprents() != null && stnext.getExprents().isEmpty()) {\n            stnext = stnext.getAllSuccessorEdges().get(0).getDestination();\n          }\n          processEdgesWithNext(stt, setExplicitEdges(stt), stnext);\n        }\n\n        int last = swst.getCaseStatements().size() - 1;\n        if (last >= 0) { // empty switch possible\n          Statement stlast = swst.getCaseStatements().get(last);\n          if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) {\n            StatEdge edge = stlast.getAllSuccessorEdges().get(0);\n            mapEdges.put(edge.getDestination(), new ArrayList<>(Collections.singletonList(edge)));\n          }\n          else {\n            mapEdges = setExplicitEdges(stlast);\n            processEdgesWithNext(stlast, mapEdges, null);\n          }\n        }\n      }\n      case SYNCHRONIZED -> {\n        SynchronizedStatement synstat = (SynchronizedStatement)stat;\n\n        processEdgesWithNext(synstat.getFirst(), setExplicitEdges(stat.getFirst()), synstat.getBody()); // FIXME: basic block?\n        mapEdges = setExplicitEdges(synstat.getBody());\n        processEdgesWithNext(synstat.getBody(), mapEdges, null);\n      }\n    }\n\n\n    return mapEdges;\n  }\n\n  private static void processEdgesWithNext(Statement stat, HashMap<Statement, List<StatEdge>> mapEdges, Statement next) {\n\n    StatEdge statedge = null;\n\n    List<StatEdge> lstSuccs = stat.getAllSuccessorEdges();\n    if (!lstSuccs.isEmpty()) {\n      statedge = lstSuccs.get(0);\n\n      if (statedge.getDestination() == next) {\n        statedge.explicit = false;\n        statedge = null;\n      }\n      else {\n        next = statedge.getDestination();\n      }\n    }\n\n    // no next for a do statement\n    if (stat.type == StatementType.DO && ((DoStatement)stat).getLoopType() == LoopType.DO) {\n      next = null;\n    }\n\n    if (next == null) {\n      if (mapEdges.size() == 1) {\n        List<StatEdge> lstEdges = mapEdges.values().iterator().next();\n        if (lstEdges.size() > 1 && mapEdges.keySet().iterator().next().type != StatementType.DUMMY_EXIT) {\n          StatEdge edge_example = lstEdges.get(0);\n\n          Statement closure = stat.getParent();\n          if (!closure.containsStatementStrict(edge_example.closure)) {\n            closure = edge_example.closure;\n          }\n\n          StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure);\n          stat.addSuccessor(newedge);\n\n          for (StatEdge edge : lstEdges) {\n            edge.explicit = false;\n          }\n\n          mapEdges.put(newedge.getDestination(), new ArrayList<>(Collections.singletonList(newedge)));\n        }\n      }\n    }\n    else {\n\n      boolean implfound = false;\n\n      for (Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) {\n        if (entr.getKey() == next) {\n          for (StatEdge edge : entr.getValue()) {\n            edge.explicit = false;\n          }\n          implfound = true;\n          break;\n        }\n      }\n\n      if (stat.getAllSuccessorEdges().isEmpty() && !implfound) {\n        List<StatEdge> lstEdges = null;\n        for (Entry<Statement, List<StatEdge>> entr : mapEdges.entrySet()) {\n          if (entr.getKey().type != StatementType.DUMMY_EXIT &&\n              (lstEdges == null || entr.getValue().size() > lstEdges.size())) {\n            lstEdges = entr.getValue();\n          }\n        }\n\n        if (lstEdges != null && lstEdges.size() > 1) {\n          StatEdge edge_example = lstEdges.get(0);\n\n          Statement closure = stat.getParent();\n          if (!closure.containsStatementStrict(edge_example.closure)) {\n            closure = edge_example.closure;\n          }\n\n          StatEdge newedge = new StatEdge(edge_example.getType(), stat, edge_example.getDestination(), closure);\n          stat.addSuccessor(newedge);\n\n          for (StatEdge edge : lstEdges) {\n            edge.explicit = false;\n          }\n        }\n      }\n\n      mapEdges.clear();\n    }\n\n    if (statedge != null) {\n      mapEdges.put(statedge.getDestination(), new ArrayList<>(Collections.singletonList(statedge)));\n    }\n  }\n\n  private static void hideDefaultSwitchEdges(Statement stat) {\n\n    if (stat.type == StatementType.SWITCH) {\n      SwitchStatement swst = (SwitchStatement)stat;\n\n      int last = swst.getCaseStatements().size() - 1;\n      if (last >= 0) { // empty switch possible\n        Statement stlast = swst.getCaseStatements().get(last);\n\n        if (stlast.getExprents() != null && stlast.getExprents().isEmpty()) {\n          if (!stlast.getAllSuccessorEdges().get(0).explicit) {\n            List<StatEdge> lstEdges = swst.getCaseEdges().get(last);\n            lstEdges.remove(swst.getDefaultEdge());\n\n            if (lstEdges.isEmpty()) {\n              swst.getCaseStatements().remove(last);\n              swst.getCaseEdges().remove(last);\n            }\n          }\n        }\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      hideDefaultSwitchEdges(st);\n    }\n  }\n\n  private static class LabelSets {\n    private final Set<Statement> breaks = new HashSet<>();\n    private final Set<Statement> continues = new HashSet<>();\n  }\n\n  private static LabelSets processStatementLabel(Statement stat) {\n    LabelSets sets = new LabelSets();\n\n    if (stat.getExprents() == null) {\n      for (Statement st : stat.getStats()) {\n        LabelSets nested = processStatementLabel(st);\n        sets.breaks.addAll(nested.breaks);\n        sets.continues.addAll(nested.continues);\n      }\n\n      boolean shieldType = (stat.type == StatementType.DO || stat.type == StatementType.SWITCH);\n      if (shieldType) {\n        for (StatEdge edge : stat.getLabelEdges()) {\n          if (edge.explicit && ((edge.getType() == EdgeType.BREAK && sets.breaks.contains(edge.getSource())) ||\n                                (edge.getType() == EdgeType.CONTINUE && sets.continues.contains(edge.getSource())))) {\n            edge.labeled = false;\n          }\n        }\n      }\n\n      switch (stat.type) {\n        case DO:\n          sets.continues.clear();\n        case SWITCH:\n          sets.breaks.clear();\n      }\n    }\n\n    sets.breaks.add(stat);\n    sets.continues.add(stat);\n\n    return sets;\n  }\n\n  public static void replaceContinueWithBreak(Statement stat) {\n\n    if (stat.type == StatementType.DO) {\n\n      List<StatEdge> lst = stat.getPredecessorEdges(EdgeType.CONTINUE);\n\n      for (StatEdge edge : lst) {\n\n        if (edge.explicit) {\n          Statement minclosure = getMinContinueClosure(edge);\n\n          if (minclosure != edge.closure &&\n              !InlineSingleBlockHelper.isBreakEdgeLabeled(edge.getSource(), minclosure)) {\n            edge.getSource().changeEdgeType(EdgeDirection.FORWARD, edge, EdgeType.BREAK);\n            edge.labeled = false;\n            minclosure.addLabeledEdge(edge);\n          }\n        }\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      replaceContinueWithBreak(st);\n    }\n  }\n\n  private static Statement getMinContinueClosure(StatEdge edge) {\n\n    Statement closure = edge.closure;\n    while (true) {\n\n      boolean found = false;\n\n      for (Statement st : closure.getStats()) {\n        if (st.containsStatementStrict(edge.getSource())) {\n          if (MergeHelper.isDirectPath(st, edge.getDestination())) {\n            closure = st;\n            found = true;\n            break;\n          }\n        }\n      }\n\n      if (!found) {\n        break;\n      }\n    }\n\n    return closure;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Set;\n\n\npublic final class LoopExtractHelper {\n\n\n  public static boolean extractLoops(Statement root) {\n\n    boolean res = (extractLoopsRec(root) != 0);\n\n    if (res) {\n      SequenceHelper.condenseSequences(root);\n    }\n\n    return res;\n  }\n\n\n  private static int extractLoopsRec(Statement stat) {\n\n    boolean res = false;\n\n    while (true) {\n\n      boolean updated = false;\n\n      for (Statement st : stat.getStats()) {\n        int extr = extractLoopsRec(st);\n        res |= (extr != 0);\n\n        if (extr == 2) {\n          updated = true;\n          break;\n        }\n      }\n\n      if (!updated) {\n        break;\n      }\n    }\n\n    if (stat.type == StatementType.DO) {\n      if (extractLoop((DoStatement)stat)) {\n        return 2;\n      }\n    }\n\n    return res ? 1 : 0;\n  }\n\n  private static boolean extractLoop(DoStatement stat) {\n    if (stat.getLoopType() != LoopType.DO) {\n      return false;\n    }\n\n    for (StatEdge edge : stat.getLabelEdges()) {\n      if (edge.getType() != EdgeType.CONTINUE && edge.getDestination().type != StatementType.DUMMY_EXIT) {\n        return false;\n      }\n    }\n\n    return extractLastIf(stat) || extractFirstIf(stat);\n  }\n\n  private static boolean extractLastIf(DoStatement stat) {\n\n    // search for an if condition at the end of the loop\n    Statement last = stat.getFirst();\n    while (last.type == StatementType.SEQUENCE) {\n      last = last.getStats().getLast();\n    }\n\n    if (last.type == StatementType.IF) {\n      IfStatement lastif = (IfStatement)last;\n      if (lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() != null) {\n        Statement ifstat = lastif.getIfstat();\n        StatEdge elseedge = lastif.getAllSuccessorEdges().get(0);\n\n        if (elseedge.getType() == EdgeType.CONTINUE && elseedge.closure == stat) {\n\n          Set<Statement> set = stat.getNeighboursSet(EdgeType.CONTINUE, EdgeDirection.BACKWARD);\n          set.remove(last);\n\n          if (set.isEmpty()) { // no direct continues in a do{}while loop\n            if (isExternStatement(stat, ifstat, ifstat)) {\n              extractIfBlock(stat, lastif);\n              return true;\n            }\n          }\n        }\n      }\n    }\n    return false;\n  }\n\n  private static boolean extractFirstIf(DoStatement stat) {\n\n    // search for an if condition at the entrance of the loop\n    Statement first = stat.getFirst();\n    while (first.type == StatementType.SEQUENCE) {\n      first = first.getFirst();\n    }\n\n    // found an if statement\n    if (first.type == StatementType.IF) {\n      IfStatement firstif = (IfStatement)first;\n\n      if (firstif.getFirst().getExprents().isEmpty()) {\n\n        if (firstif.iftype == IfStatement.IFTYPE_IF && firstif.getIfstat() != null) {\n          Statement ifstat = firstif.getIfstat();\n\n          if (isExternStatement(stat, ifstat, ifstat)) {\n            extractIfBlock(stat, firstif);\n            return true;\n          }\n        }\n      }\n    }\n    return false;\n  }\n\n\n  private static boolean isExternStatement(DoStatement loop, Statement block, Statement stat) {\n\n    for (StatEdge edge : stat.getAllSuccessorEdges()) {\n      if (loop.containsStatement(edge.getDestination()) &&\n          !block.containsStatement(edge.getDestination())) {\n        return false;\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      if (!isExternStatement(loop, block, st)) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n\n  private static void extractIfBlock(DoStatement loop, IfStatement ifstat) {\n\n    Statement target = ifstat.getIfstat();\n    StatEdge ifedge = ifstat.getIfEdge();\n\n    ifstat.setIfstat(null);\n    ifedge.getSource().changeEdgeType(EdgeDirection.FORWARD, ifedge, EdgeType.BREAK);\n    ifedge.closure = loop;\n    ifstat.getStats().removeWithKey(target.id);\n\n    loop.addLabeledEdge(ifedge);\n\n    SequenceStatement block = new SequenceStatement(Arrays.asList(loop, target));\n    loop.getParent().replaceStatement(loop, block);\n    block.setAllParent();\n\n    loop.addSuccessor(new StatEdge(EdgeType.REGULAR, loop, target));\n\n    for (StatEdge edge : new ArrayList<>(block.getLabelEdges())) {\n      if (edge.getType() == EdgeType.CONTINUE || edge == ifedge) {\n        loop.addLabeledEdge(edge);\n      }\n    }\n\n    for (StatEdge edge : block.getPredecessorEdges(EdgeType.CONTINUE)) {\n      if (loop.containsStatementStrict(edge.getSource())) {\n        block.removePredecessor(edge);\n        edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, loop);\n        loop.addPredecessor(edge);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport static org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\n\npublic final class MergeHelper {\n  public static void enhanceLoops(Statement root) {\n    while (enhanceLoopsRec(root)) /**/;\n    SequenceHelper.condenseSequences(root);\n  }\n\n  private static boolean enhanceLoopsRec(Statement stat) {\n    boolean res = false;\n\n    for (Statement st : stat.getStats()) {\n      if (st.getExprents() == null) {\n        res |= enhanceLoopsRec(st);\n      }\n    }\n\n    if (stat.type == StatementType.DO) {\n      res |= enhanceLoop((DoStatement)stat);\n    }\n\n    return res;\n  }\n\n  private static boolean enhanceLoop(DoStatement stat) {\n    LoopType oldLoop = stat.getLoopType();\n\n    switch (oldLoop) {\n      case DO -> {\n\n        // identify a while loop\n        if (matchWhile(stat)) {\n          // identify a for loop - subtype of while\n          matchFor(stat);\n        }\n        else {\n          // identify a do{}while loop\n          matchDoWhile(stat);\n        }\n      }\n      case WHILE -> matchFor(stat);\n    }\n\n    return (stat.getLoopType() != oldLoop);\n  }\n\n  private static void matchDoWhile(DoStatement stat) {\n    // search for an if condition at the end of the loop\n    Statement last = stat.getFirst();\n    while (last.type == StatementType.SEQUENCE) {\n      last = last.getStats().getLast();\n    }\n\n    if (last.type == StatementType.IF) {\n      IfStatement lastif = (IfStatement)last;\n      if (lastif.iftype == IfStatement.IFTYPE_IF && lastif.getIfstat() == null) {\n        StatEdge ifedge = lastif.getIfEdge();\n        StatEdge elseedge = lastif.getAllSuccessorEdges().get(0);\n\n        if ((ifedge.getType() == EdgeType.BREAK && elseedge.getType() == EdgeType.CONTINUE && elseedge.closure == stat\n             && isDirectPath(stat, ifedge.getDestination())) ||\n            (ifedge.getType() == EdgeType.CONTINUE && elseedge.getType() == EdgeType.BREAK && ifedge.closure == stat\n             && isDirectPath(stat, elseedge.getDestination()))) {\n\n          Set<Statement> set = stat.getNeighboursSet(EdgeType.CONTINUE, EdgeDirection.BACKWARD);\n          set.remove(last);\n\n          if (!set.isEmpty()) {\n            return;\n          }\n\n          stat.setLoopType(LoopType.DO_WHILE);\n\n          IfExprent ifexpr = (IfExprent)lastif.getHeadexprent().copy();\n          if (ifedge.getType() == EdgeType.BREAK) {\n            ifexpr.negateIf();\n          }\n          stat.setConditionExprent(ifexpr.getCondition());\n          lastif.getFirst().removeSuccessor(ifedge);\n          lastif.removeSuccessor(elseedge);\n\n          // remove empty if\n          if (lastif.getFirst().getExprents().isEmpty()) {\n            removeLastEmptyStatement(stat, lastif);\n          }\n          else {\n            lastif.setExprents(lastif.getFirst().getExprents());\n\n            StatEdge newedge = new StatEdge(EdgeType.CONTINUE, lastif, stat);\n            lastif.addSuccessor(newedge);\n            stat.addLabeledEdge(newedge);\n          }\n\n          if (stat.getAllSuccessorEdges().isEmpty()) {\n            StatEdge edge = elseedge.getType() == EdgeType.CONTINUE ? ifedge : elseedge;\n\n            edge.setSource(stat);\n            if (edge.closure == stat) {\n              edge.closure = stat.getParent();\n            }\n            stat.addSuccessor(edge);\n          }\n        }\n      }\n    }\n  }\n\n  private static boolean matchWhile(DoStatement stat) {\n\n    // search for an if condition at the entrance of the loop\n    Statement first = stat.getFirst();\n    while (first.type == StatementType.SEQUENCE) {\n      first = first.getFirst();\n    }\n\n    // found an if statement\n    if (first.type == StatementType.IF) {\n      IfStatement firstif = (IfStatement)first;\n\n      if (firstif.getFirst().getExprents().isEmpty()) {\n\n        if (firstif.iftype == IfStatement.IFTYPE_IF) {\n          if (firstif.getIfstat() == null) {\n            StatEdge ifedge = firstif.getIfEdge();\n            if (isDirectPath(stat, ifedge.getDestination())) {\n              // exit condition identified\n              stat.setLoopType(LoopType.WHILE);\n\n              // negate condition (while header)\n              IfExprent ifexpr = (IfExprent)firstif.getHeadexprent().copy();\n              ifexpr.negateIf();\n              stat.setConditionExprent(ifexpr.getCondition());\n\n              // remove edges\n              firstif.getFirst().removeSuccessor(ifedge);\n              firstif.removeSuccessor(firstif.getAllSuccessorEdges().get(0));\n\n              if (stat.getAllSuccessorEdges().isEmpty()) {\n                ifedge.setSource(stat);\n                if (ifedge.closure == stat) {\n                  ifedge.closure = stat.getParent();\n                }\n                stat.addSuccessor(ifedge);\n              }\n\n              // remove empty if statement as it is now part of the loop\n              if (firstif == stat.getFirst()) {\n                BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(\n                  DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));\n                bstat.setExprents(new ArrayList<>());\n                stat.replaceStatement(firstif, bstat);\n              }\n              else {\n                // precondition: sequence must contain more than one statement!\n                Statement sequence = firstif.getParent();\n                sequence.getStats().removeWithKey(firstif.id);\n                sequence.setFirst(sequence.getStats().get(0));\n              }\n\n              return true;\n            }\n          }\n          else {\n            StatEdge elseedge = firstif.getAllSuccessorEdges().get(0);\n            if (isDirectPath(stat, elseedge.getDestination())) {\n              // exit condition identified\n              stat.setLoopType(LoopType.WHILE);\n\n              // no need to negate the while condition\n              stat.setConditionExprent(((IfExprent)firstif.getHeadexprent().copy()).getCondition());\n\n              // remove edges\n              StatEdge ifedge = firstif.getIfEdge();\n              firstif.getFirst().removeSuccessor(ifedge);\n              firstif.removeSuccessor(elseedge);\n\n              if (stat.getAllSuccessorEdges().isEmpty()) {\n\n                elseedge.setSource(stat);\n                if (elseedge.closure == stat) {\n                  elseedge.closure = stat.getParent();\n                }\n                stat.addSuccessor(elseedge);\n              }\n\n              if (firstif.getIfstat() == null) {\n                BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(\n                  DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));\n                bstat.setExprents(new ArrayList<>());\n\n                ifedge.setSource(bstat);\n                bstat.addSuccessor(ifedge);\n\n                stat.replaceStatement(firstif, bstat);\n              }\n              else {\n                // replace the if statement with its content\n                first.getParent().replaceStatement(first, firstif.getIfstat());\n\n                // lift closures\n                for (StatEdge prededge : elseedge.getDestination().getPredecessorEdges(EdgeType.BREAK)) {\n                  if (stat.containsStatementStrict(prededge.closure)) {\n                    stat.addLabeledEdge(prededge);\n                  }\n                }\n\n                LabelHelper.lowClosures(stat);\n              }\n\n              return true;\n            }\n          }\n        }\n      }\n    }\n    return false;\n  }\n\n  public static boolean isDirectPath(Statement stat, Statement endstat) {\n\n    Set<Statement> setStat = stat.getNeighboursSet(EdgeType.DIRECT_ALL, EdgeDirection.FORWARD);\n    if (setStat.isEmpty()) {\n      Statement parent = stat.getParent();\n      if (parent == null) {\n        return false;\n      }\n      else {\n        switch (parent.type) {\n          case ROOT:\n            return endstat.type == StatementType.DUMMY_EXIT;\n          case DO:\n            return (endstat == parent);\n          case SWITCH:\n            SwitchStatement swst = (SwitchStatement)parent;\n            for (int i = 0; i < swst.getCaseStatements().size() - 1; i++) {\n              Statement stt = swst.getCaseStatements().get(i);\n              if (stt == stat) {\n                Statement stnext = swst.getCaseStatements().get(i + 1);\n\n                if (stnext.getExprents() != null && stnext.getExprents().isEmpty()) {\n                  stnext = stnext.getAllSuccessorEdges().get(0).getDestination();\n                }\n                return (endstat == stnext);\n              }\n            }\n          default:\n            return isDirectPath(parent, endstat);\n        }\n      }\n    }\n    else {\n      return setStat.contains(endstat);\n    }\n  }\n\n  private static void matchFor(DoStatement stat) {\n    Exprent lastDoExprent, initDoExprent;\n    Statement lastData, preData = null;\n\n    // get last exprent\n    lastData = getLastDirectData(stat.getFirst());\n    if (lastData == null || lastData.getExprents().isEmpty()) {\n      return;\n    }\n\n    List<Exprent> lstExpr = lastData.getExprents();\n    lastDoExprent = lstExpr.get(lstExpr.size() - 1);\n\n    boolean issingle = false;\n    if (lstExpr.size() == 1) {  // single exprent\n      if (lastData.getAllPredecessorEdges().size() > 1) { // break edges\n        issingle = true;\n      }\n    }\n\n    boolean haslast = issingle || lastDoExprent.type == Exprent.EXPRENT_ASSIGNMENT || lastDoExprent.type == Exprent.EXPRENT_FUNCTION;\n    if (!haslast) {\n      return;\n    }\n\n    boolean hasinit = false;\n\n    // search for an initializing exprent\n    Statement current = stat;\n    while (true) {\n      Statement parent = current.getParent();\n      if (parent == null) {\n        break;\n      }\n\n      if (parent.type == StatementType.SEQUENCE) {\n        if (current == parent.getFirst()) {\n          current = parent;\n        }\n        else {\n          preData = current.getNeighbours(EdgeType.REGULAR, EdgeDirection.BACKWARD).get(0);\n          preData = getLastDirectData(preData);\n          if (preData != null && !preData.getExprents().isEmpty()) {\n            initDoExprent = preData.getExprents().get(preData.getExprents().size() - 1);\n            if (initDoExprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n              hasinit = true;\n            }\n          }\n          break;\n        }\n      }\n      else {\n        break;\n      }\n    }\n\n    if (hasinit || issingle) {  // FIXME: issingle sufficient?\n      Set<Statement> set = stat.getNeighboursSet(EdgeType.CONTINUE, EdgeDirection.BACKWARD);\n      set.remove(lastData);\n\n      if (!set.isEmpty()) {\n        return;\n      }\n\n      stat.setLoopType(LoopType.FOR);\n      if (hasinit) {\n        stat.setInitExprent(preData.getExprents().remove(preData.getExprents().size() - 1));\n      }\n      stat.setIncExprent(lastData.getExprents().remove(lastData.getExprents().size() - 1));\n    }\n\n    if (lastData.getExprents().isEmpty()) {\n      List<StatEdge> lst = lastData.getAllSuccessorEdges();\n      if (!lst.isEmpty()) {\n        lastData.removeSuccessor(lst.get(0));\n      }\n      removeLastEmptyStatement(stat, lastData);\n    }\n  }\n\n  private static void removeLastEmptyStatement(DoStatement dostat, Statement stat) {\n\n    if (stat == dostat.getFirst()) {\n      BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(\n        DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));\n      bstat.setExprents(new ArrayList<>());\n      dostat.replaceStatement(stat, bstat);\n    }\n    else {\n      for (StatEdge edge : stat.getAllPredecessorEdges()) {\n        edge.getSource().changeEdgeType(EdgeDirection.FORWARD, edge, EdgeType.CONTINUE);\n\n        stat.removePredecessor(edge);\n        edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, dostat);\n        dostat.addPredecessor(edge);\n\n        dostat.addLabeledEdge(edge);\n      }\n\n      // parent is a sequence statement\n      stat.getParent().getStats().removeWithKey(stat.id);\n    }\n  }\n\n  private static Statement getLastDirectData(Statement stat) {\n    if (stat.getExprents() != null) {\n      return stat;\n    }\n\n    if (stat.type == StatementType.SEQUENCE) {\n      for (int i = stat.getStats().size() - 1; i >= 0; i--) {\n        Statement tmp = getLastDirectData(stat.getStats().get(i));\n        if (tmp == null || !tmp.getExprents().isEmpty()) {\n          return tmp;\n        }\n      }\n    }\n    return null;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\n\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\n\npublic class PPandMMHelper {\n\n  private boolean exprentReplaced;\n\n  public boolean findPPandMM(RootStatement root) {\n\n    FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();\n    DirectGraph dgraph = flatthelper.buildDirectGraph(root);\n\n    LinkedList<DirectNode> stack = new LinkedList<>();\n    stack.add(dgraph.first);\n\n    HashSet<DirectNode> setVisited = new HashSet<>();\n\n    boolean res = false;\n\n    while (!stack.isEmpty()) {\n\n      DirectNode node = stack.removeFirst();\n\n      if (setVisited.contains(node)) {\n        continue;\n      }\n      setVisited.add(node);\n\n      res |= processExprentList(node.exprents);\n\n      stack.addAll(node.successors);\n    }\n\n    return res;\n  }\n\n  private boolean processExprentList(List<Exprent> lst) {\n\n    boolean result = false;\n\n    for (int i = 0; i < lst.size(); i++) {\n      Exprent exprent = lst.get(i);\n      exprentReplaced = false;\n\n      Exprent retexpr = processExprentRecursive(exprent);\n      if (retexpr != null) {\n        lst.set(i, retexpr);\n\n        result = true;\n        i--; // process the same exprent again\n      }\n\n      result |= exprentReplaced;\n    }\n\n    return result;\n  }\n\n  private Exprent processExprentRecursive(Exprent exprent) {\n\n    boolean replaced = true;\n    while (replaced) {\n      replaced = false;\n\n      for (Exprent expr : exprent.getAllExprents()) {\n        Exprent retexpr = processExprentRecursive(expr);\n        if (retexpr != null) {\n          exprent.replaceExprent(expr, retexpr);\n          replaced = true;\n          exprentReplaced = true;\n          break;\n        }\n      }\n    }\n\n    if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent as = (AssignmentExprent)exprent;\n\n      if (as.getRight().type == Exprent.EXPRENT_FUNCTION) {\n        FunctionExprent func = (FunctionExprent)as.getRight();\n\n        VarType midlayer = null;\n        if (func.getFuncType() >= FunctionExprent.FUNCTION_I2L &&\n            func.getFuncType() <= FunctionExprent.FUNCTION_I2S) {\n          midlayer = func.getSimpleCastType();\n          if (func.getLstOperands().get(0).type == Exprent.EXPRENT_FUNCTION) {\n            func = (FunctionExprent)func.getLstOperands().get(0);\n          }\n          else {\n            return null;\n          }\n        }\n\n        if (func.getFuncType() == FunctionExprent.FUNCTION_ADD ||\n            func.getFuncType() == FunctionExprent.FUNCTION_SUB) {\n          Exprent econd = func.getLstOperands().get(0);\n          Exprent econst = func.getLstOperands().get(1);\n\n          if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST &&\n              func.getFuncType() == FunctionExprent.FUNCTION_ADD) {\n            econd = econst;\n            econst = func.getLstOperands().get(0);\n          }\n\n          if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) {\n            Exprent left = as.getLeft();\n\n            VarType condtype = econd.getExprType();\n            if (left.equals(econd) && (midlayer == null || midlayer.equals(condtype))) {\n              FunctionExprent ret = new FunctionExprent(\n                func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI,\n                econd, func.bytecode);\n              ret.setImplicitType(condtype);\n\n              exprentReplaced = true;\n              return ret;\n            }\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/PatternHelper.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\n\nimport static org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper.TempVarAssignmentItem;\nimport static org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper.removeTempVariableDeclarations;\nimport static org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent.EXIT_THROW;\n\npublic final class PatternHelper {\n\n  public static final String MATCH_EXCEPTION = \"java/lang/MatchException\";\n\n  /**\n   * Method searches if-pattern like <code>if (var instanceof SomeType)</code> pattern,\n   * and assignment expression pattern like <code>SomeType s = (SomeType)var;</code>.\n   * If the pattern were found, then method replaces found assignment expression with pattern variable.\n   *\n   * @param statement root statement to start traversal\n   * @param structClass owner class of <code>statement</code>\n   */\n  public static void replaceAssignmentsWithPatternVariables(@NotNull RootStatement statement, @NotNull StructClass structClass) {\n    if (!structClass.hasPatternsInInstanceofSupport()) return;\n    boolean recordPatternSupport = structClass.hasRecordPatternSupport();\n    List<TempVarAssignmentItem> tempVarAssignments = new ArrayList<>();\n\n    List<Runnable> runnables = replaceAssignmentsWithPatternVariables(statement, new HashSet<>(), tempVarAssignments, recordPatternSupport);\n    if (runnables.isEmpty() || !SwitchHelper.checkAssignmentsToDelete(statement, tempVarAssignments)) {\n      return;\n    }\n    for (Runnable runnable : runnables) {\n      runnable.run();\n    }\n    removeTempVariableDeclarations(tempVarAssignments);\n  }\n\n  /**\n   * Replaces assignments with pattern variables in the provided statement.\n   *\n   * @return a list of Runnable objects containing the actions to replace assignments with pattern variables\n   */\n  private static List<Runnable> replaceAssignmentsWithPatternVariables(@NotNull Statement statement,\n                                                                       @NotNull Set<IfStatement> usedIfStatements,\n                                                                       @NotNull List<TempVarAssignmentItem> tempVarAssignments,\n                                                                       boolean recordPatternSupport) {\n    ArrayList<Runnable> actions = new ArrayList<>();\n    if (statement instanceof IfStatement ifStatement && !usedIfStatements.contains(ifStatement)) {\n      FunctionExprent instanceOfExprent = findInstanceofExprent(ifStatement);\n      if (instanceOfExprent == null) return new ArrayList<>();\n\n      List<Exprent> operands = instanceOfExprent.getLstOperands();\n      if (operands.size() != 2 || operands.get(0).type != Exprent.EXPRENT_VAR || operands.get(1).type != Exprent.EXPRENT_CONST) return new ArrayList<>();\n      VarExprent operand = (VarExprent)operands.get(0);\n      ConstExprent checkType = (ConstExprent)operands.get(1);\n\n      if (ifStatement.getIfstat() == null) {\n        return new ArrayList<>();\n      }\n      Statement statementToChange = ifStatement.getIfstat();\n      PatternVariableCandidate patternVarCandidate = findInitPatternVarCandidate(statementToChange, operand, checkType, recordPatternSupport, statementToChange);\n      if (patternVarCandidate == null && ifStatement.getElsestat() != null) {\n        statementToChange = ifStatement.getElsestat();\n        patternVarCandidate = findInitPatternVarCandidate(statementToChange, operand, checkType, recordPatternSupport, statementToChange);\n      }\n      if (patternVarCandidate == null) return new ArrayList<>();\n      tempVarAssignments.addAll(patternVarCandidate.getTempAssignments());\n      usedIfStatements.add(ifStatement);\n      usedIfStatements.addAll(patternVarCandidate.getUsedIfStatement());\n      PatternVariableCandidate finalPatternVarCandidate = patternVarCandidate;\n      Runnable action = ()-> {\n        operands.remove(1);\n        operands.add(finalPatternVarCandidate.getVarExprent());\n        finalPatternVarCandidate.getCleaner().run();\n      };\n      actions.add(action);\n    }\n    for (Statement child : statement.getStats()) {\n      actions.addAll(0, replaceAssignmentsWithPatternVariables(child, usedIfStatements, tempVarAssignments, recordPatternSupport));\n    }\n    return actions;\n  }\n\n  @Nullable\n  private static FunctionExprent findInstanceofExprent(@NotNull IfStatement ifStat) {\n    return ifStat.getHeadexprent().getAllExprents(true).stream()\n      .filter(expr -> expr.type == Exprent.EXPRENT_FUNCTION).map(expr -> (FunctionExprent)expr)\n      .filter(expr -> expr.getFuncType() == FunctionExprent.FUNCTION_INSTANCEOF)\n      .findFirst().orElse(null);\n  }\n\n  static PatternVariableCandidate findInitPatternVarCandidate(@NotNull Statement ifElseStat,\n                                                              @NotNull VarExprent operand,\n                                                              @NotNull ConstExprent checkType,\n                                                              boolean recordPatternSupport,\n                                                              @NotNull Statement topLevelStatement) {\n    if (ifElseStat instanceof BasicBlockStatement basicBlockStatement) {\n      //check that cast correct and get the last assignment\n      PatternVariableCandidate candidate = findSimpleCandidateFromIfStat(ifElseStat, operand, checkType, topLevelStatement);\n      if (candidate == null) return null;\n      //check what if it is record patterns\n      if (recordPatternSupport && DecompilerContext.getOption(IFernflowerPreferences.CONVERT_RECORD_PATTERN)) {\n        //collect everything from zero to check\n        PatternVariableCandidate recordCandidate = findInitRecordPatternCandidate(basicBlockStatement, operand, candidate.getVarExprent());\n        if (recordCandidate != null) {\n          recordCandidate.getTempVarAssignments().addAll(candidate.getTempVarAssignments());\n          return recordCandidate;\n        }\n      }\n      return candidate;\n    }\n    else if (ifElseStat instanceof IfStatement || ifElseStat instanceof SequenceStatement) {\n      return findInitPatternVarCandidate(ifElseStat.getFirst(), operand, checkType, recordPatternSupport, topLevelStatement);\n    }\n    return null;\n  }\n\n  @Nullable\n  static PatternVariableCandidate findNextPatternVarCandidate(@NotNull Statement ifBranch,\n                                                                      @NotNull VarExprent operand,\n                                                                      @NotNull ConstExprent checkType,\n                                                                      @NotNull VarTracker varTracker,\n                                                                      @NotNull Statement topLevelStatement) {\n\n    if (ifBranch instanceof BasicBlockStatement) {\n      //check that cast correct and get the last assignment\n      PatternVariableCandidate candidate = findSimpleCandidateFromIfStat(ifBranch, operand, checkType, topLevelStatement);\n      //check what if it is record patterns\n      VarTracker newVarTracker = varTracker.copy();\n      if (newVarTracker == null) {\n        return candidate;\n      }\n      RecordVarExprent previousRecord = newVarTracker.getRecord(operand);\n      if (previousRecord == null) {\n        return candidate;\n      }\n      previousRecord.setVarType(checkType.getConstType());\n      if (!(ifBranch.getParent() instanceof IfStatement) &&  //prevent infinite recursion\n          DecompilerContext.getOption(IFernflowerPreferences.CONVERT_RECORD_PATTERN)) {\n        PatternVariableCandidate recordCandidate = findRecordPatternCandidate(ifBranch.getParent(), newVarTracker);\n        if (recordCandidate != null) {\n          varTracker.putAll(newVarTracker);\n          return recordCandidate;\n        }\n      }\n      return candidate;\n    }\n    if (ifBranch instanceof SequenceStatement || ifBranch instanceof IfStatement) {\n      return findNextPatternVarCandidate(ifBranch.getFirst(), operand, checkType, varTracker, topLevelStatement);\n    }\n    return null;\n  }\n\n  @Nullable\n  private static PatternVariableCandidate findSimpleCandidateFromIfStat(@NotNull Statement ifElseStat,\n                                                                        @NotNull VarExprent operand,\n                                                                        @NotNull ConstExprent checkType,\n                                                                        @NotNull Statement topLevelStatement) {\n    List<Exprent> ifElseExprents = ifElseStat.getExprents();\n    if (ifElseExprents==null || ifElseExprents.isEmpty() || ifElseExprents.get(0).type != Exprent.EXPRENT_ASSIGNMENT) return null;\n\n    AssignmentExprent assignmentExprent = (AssignmentExprent)ifElseExprents.get(0);\n    if (assignmentExprent.getLeft().type != Exprent.EXPRENT_VAR) return null;\n    VarExprent varExprent = (VarExprent)assignmentExprent.getLeft();\n    if (assignmentExprent.getRight().type != Exprent.EXPRENT_FUNCTION) return null;\n    FunctionExprent castExprent = (FunctionExprent)assignmentExprent.getRight();\n    if (castExprent.getFuncType() != FunctionExprent.FUNCTION_CAST) return null;\n\n    if (!varExprent.isDefinition()) {\n      Exprent leftAssignmentPart = assignmentExprent.getLeft();\n      Exprent rightAssignmentPart = assignmentExprent.getRight();\n      if (leftAssignmentPart.type != Exprent.EXPRENT_VAR || rightAssignmentPart.type != Exprent.EXPRENT_FUNCTION ||\n          ((FunctionExprent)rightAssignmentPart).getFuncType() != FunctionExprent.FUNCTION_CAST) {\n        return null;\n      }\n      varExprent = ((VarExprent)leftAssignmentPart);\n      List<Exprent> castOperands = ((FunctionExprent)rightAssignmentPart).getLstOperands();\n      if (castOperands.size() != 2 || castOperands.get(1).type != Exprent.EXPRENT_CONST) return null;\n      VarType castType = ((ConstExprent)castOperands.get(1)).getConstType();\n      varExprent = (VarExprent)varExprent.copy();\n      varExprent.setVarType(castType);\n      varExprent.setDefinition(true);\n    }\n\n    List<Exprent> castExprents = castExprent.getAllExprents();\n    if (castExprents.size() != 2 ||\n        !operand.equals(castExprents.get(0)) ||\n        !checkType.equals(castExprents.get(1))) {\n      return null;\n    }\n    List<TempVarAssignmentItem> tempVarAssignments = new ArrayList<>();\n    if (!varExprent.isDefinition()) {\n      tempVarAssignments.add(new TempVarAssignmentItem(varExprent, ifElseStat));\n    }\n    if (assignmentExprent.getLeft() instanceof VarExprent toDelete) {\n      tempVarAssignments.add(new TempVarAssignmentItem(toDelete, ifElseStat));\n    }\n    return new PatternVariableCandidate(varExprent, topLevelStatement, new HashSet<>(), tempVarAssignments, () -> {\n    });\n  }\n\n  private static PatternVariableCandidate findInitRecordPatternCandidate(@NotNull BasicBlockStatement blockStatement,\n                                                                         @NotNull VarExprent varExprent,\n                                                                         @NotNull VarExprent candidate) {\n    Statement parent = blockStatement.getParent();\n    if (!(parent instanceof SequenceStatement parentSequenceStatement)) {\n      return null;\n    }\n    int indexOfBlock = parentSequenceStatement.getStats().indexOf(blockStatement);\n    if (indexOfBlock != 0) {\n      return null;\n    }\n    RecordVarExprent recordVarExprent = new RecordVarExprent(candidate);\n    VarTracker varTracker = new VarTracker(recordVarExprent);\n    varTracker.put(varExprent, recordVarExprent, null);\n    return findRecordPatternCandidate(parentSequenceStatement, varTracker);\n  }\n\n  @Nullable\n  private static PatternVariableCandidate findRecordPatternCandidate(@NotNull Statement parent,\n                                                                     @NotNull VarTracker varTracker) {\n    if (!(parent instanceof SequenceStatement)) {\n      return null;\n    }\n    if (checkRegularEdgesForRecordPattern(parent)) return null;\n    Set<IfStatement> ifStatements = new HashSet<>();\n\n    VBStyleCollection<Statement, Integer> stats = parent.getStats();\n    if (stats.size() < 3) {\n      return null;\n    }\n    int i = 0;\n\n    //It is possible to have false positive or false negative results here, and it is hard to exclude that.\n    //Let's keep it simple at least\n    while (true) {\n      if (!(stats.size() > i + 1 && stats.get(i) instanceof BasicBlockStatement basicBlockStatement &&\n            stats.get(i + 1) instanceof CatchStatement catchStatement)) {\n        if (i != 0) {\n          break;\n        }\n        return null;\n      }\n      //the first block must contain only assignments which can be gathered\n      if (!processFullBlock(varTracker, basicBlockStatement)) {\n        return null;\n      }\n      //a catch section with call for a record component\n      if (!processCatchStatement(varTracker, catchStatement)) {\n        return null;\n      }\n      i += 2;\n    }\n    int nextIndex = i;\n    List<TempVarAssignmentItem> tempVarAssignmentItems = new ArrayList<>();\n    if (!processAtLeastOneBlock(varTracker, stats.get(nextIndex))) {\n      tempVarAssignmentItems.addAll(varTracker.getTempItems());\n      //without reassigning Object\n      SequenceStatement sequenceStatement =\n        new SequenceStatement((parent.getStats().subList(nextIndex, parent.getStats().size())));\n      return new PatternVariableCandidate(varTracker.root, sequenceStatement, new HashSet<>(), tempVarAssignmentItems, () -> {\n        sequenceStatement.getStats().forEach(stat->stat.setParent(sequenceStatement));\n        parent.getParent().replaceStatement(parent, sequenceStatement);\n      });\n    }\n    //R(String s), there is the next if-statement, which defines the next class\n    if (stats.get(nextIndex) instanceof IfStatement ifStatement && stats.size() - 1 == nextIndex) {\n      PatternVariableCandidate nestedCandidate = findRecursivelyInstanceOfIfStatement(stats.get(nextIndex), varTracker, tempVarAssignmentItems);\n      if (nestedCandidate != null) {\n        ifStatements.add(ifStatement);\n        ifStatements.addAll(nestedCandidate.getUsedIfStatement());\n        tempVarAssignmentItems.addAll(varTracker.getTempItems());\n        tempVarAssignmentItems.addAll(nestedCandidate.getTempVarAssignments());\n        return new PatternVariableCandidate(varTracker.root, nestedCandidate.getNextStatement(), ifStatements, tempVarAssignmentItems, () -> {\n          nestedCandidate.getCleaner().run();\n          parent.getParent().replaceStatement(parent, ifStatement.getIfstat());\n        });\n      }\n    }\n    //R(Object o), without defining the next class.\n    tempVarAssignmentItems.addAll(varTracker.getTempItems());\n    SequenceStatement sequenceStatement =\n      new SequenceStatement((parent.getStats().subList(nextIndex, parent.getStats().size())));\n    return new PatternVariableCandidate(varTracker.root, sequenceStatement, new HashSet<>(), tempVarAssignmentItems, () -> {\n      sequenceStatement.getStats().forEach(stat->stat.setParent(sequenceStatement));\n      parent.getParent().replaceStatement(parent, sequenceStatement);\n    });\n  }\n\n  private static PatternVariableCandidate findRecursivelyInstanceOfIfStatement(@NotNull Statement statement,\n                                                                               @NotNull VarTracker tracker,\n                                                                               @NotNull List<TempVarAssignmentItem> tempVarAssignmentItems) {\n    if (!(statement instanceof IfStatement ifStatement)) {\n      return null;\n    }\n    if (ifStatement.isNegated() || ifStatement.getHeadexprent().getAllExprents().size() != 1) {\n      return null;\n    }\n    FunctionExprent instanceOfExprent = findInstanceofExprent(ifStatement);\n    if (instanceOfExprent == null) {\n      return null;\n    }\n    List<Exprent> operands = instanceOfExprent.getLstOperands();\n    if (operands.size() != 2 || operands.get(0).type != Exprent.EXPRENT_VAR || operands.get(1).type != Exprent.EXPRENT_CONST) {\n      return null;\n    }\n    VarExprent operand = (VarExprent)operands.get(0);\n    ConstExprent checkType = (ConstExprent)operands.get(1);\n    RecordVarExprent recordVarExprent = tracker.getRecord(operand);\n    if (recordVarExprent == null) {\n      return null;\n    }\n\n    PatternVariableCandidate candidate = findNextPatternVarCandidate(ifStatement.getIfstat(), operand, checkType, tracker, ifStatement.getIfstat());\n    if (candidate == null) {\n      return null;\n    }\n    if (!recordVarExprent.copyFrom(candidate.getVarExprent())) {\n      return null;\n    }\n    tempVarAssignmentItems.addAll(candidate.getTempVarAssignments());\n    tracker.put(candidate.getVarExprent(), recordVarExprent, null);\n    return candidate;\n  }\n\n  /**\n   * Processes a catch statement to check if it matches a specific pattern that involves\n   * record assignments. If the pattern matches, the method updates the given VarTracker\n   * with the record assignment information.\n   * Expected pattern:\n   * try{\n   *   Object o = record.component();\n   * } catch(Throwable e){\n   *   throw new MatchException(_, _);\n   * }\n   *\n   * @param tracker   the VarTracker used to track record assignments\n   * @param statement the CatchStatement to process\n   * @return true if the catch statement matches the pattern and was successfully processed,\n   *         false otherwise\n   */\n  private static boolean processCatchStatement(@NotNull VarTracker tracker,\n                                               @NotNull CatchStatement statement) {\n    Statement tryBody = statement.getFirst();\n    if (tryBody == null || tryBody.getStats() == null || !tryBody.getStats().isEmpty() ||\n        tryBody.getExprents() == null || tryBody.getExprents().size() != 1) {\n      return false;\n    }\n    Exprent body = tryBody.getExprents().get(0);\n    InvocationExprent invocationExprent = null;\n    boolean hasAssignment = false;\n    if (body instanceof AssignmentExprent assignmentExprent && assignmentExprent.getRight() instanceof InvocationExprent newInvocation) {\n      invocationExprent = newInvocation;\n      hasAssignment = true;\n    }\n    else if (body instanceof InvocationExprent newInvocation) {\n      invocationExprent = newInvocation;\n    }\n    if (invocationExprent == null) {\n      return false;\n    }\n    Exprent qualifier = invocationExprent.getInstance();\n    if (qualifier == null ||\n        invocationExprent.isStatic() ||\n        !(invocationExprent.getParameters() != null && invocationExprent.getParameters().isEmpty())) {\n      return false;\n    }\n    RecordVarExprent recordVarExprent = tracker.getRecord(qualifier);\n    if (recordVarExprent == null) {\n      return false;\n    }\n    if (statement.getStats().size() != 2) {\n      return false;\n    }\n    Statement catchSection = statement.getStats().get(1);\n    if (!(catchSection.getStats().isEmpty() && catchSection.getExprents() != null)) {\n      return false;\n    }\n    if (!((catchSection.getExprents().size() == 1) ||\n          (catchSection.getExprents().size() == 2 && catchSection.getExprents().get(1) instanceof ExitExprent))) {\n      return false;\n    }\n    Exprent throwExpected = catchSection.getExprents().get(0);\n    if (!((throwExpected instanceof ExitExprent exitExprent && exitExprent.getExitType() == EXIT_THROW &&\n          exitExprent.getValue() instanceof NewExprent newExprent && newExprent.getNewType() != null &&\n          MATCH_EXCEPTION.equals(newExprent.getNewType().getValue())) ||\n        (catchSection.getExprents().get(1) instanceof ExitExprent))) {\n      return false;\n    }\n\n    if (!hasAssignment) {\n      VarType exprType = invocationExprent.getExprType();\n      VarProcessor processor = DecompilerContext.getVarProcessor();\n      RecordVarExprent nextComponent = new RecordVarExprent(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER), exprType,processor ));\n      recordVarExprent.addComponent(nextComponent);\n      return true;\n    }\n    if (!(((AssignmentExprent)body).getLeft() instanceof VarExprent assignTo)) {\n      return false;\n    }\n    VarExprent assignToDefinition = (VarExprent)assignTo.copy();\n    assignToDefinition.setDefinition(true);\n    RecordVarExprent nextComponent = new RecordVarExprent(assignToDefinition);\n    recordVarExprent.addComponent(nextComponent);\n    tracker.put(assignTo, nextComponent, tryBody);\n\n    return true;\n  }\n\n  /**\n   * Process all exprents in a statement, collecting record assignments in the given statement into the tracker.\n   *\n   * @param tracker    the VarTracker to collect record assignments\n   * @param statement  the Statement to process\n   * @return true if the process completed successfully, false otherwise\n   */\n  private static boolean processFullBlock(@NotNull VarTracker tracker,\n                                          @NotNull Statement statement) {\n    if (statement instanceof BasicBlockStatement && statement.getExprents() != null) {\n      for (Exprent statementExprent : statement.getExprents()) {\n        if (!collectRecordAssignment(tracker, statementExprent, statement)) return false;\n      }\n    }\n    return true;\n  }\n\n  /**\n   * Process at least first exprent in a statement.\n   * Collects record assignments in the given statement into the tracker.\n   *\n   * @param tracker   the VarTracker to collect record assignments\n   * @param statement the Statement to process\n   * @return true if at least first exprent is processed, false otherwise.\n   */\n  static boolean processAtLeastOneBlock(@NotNull VarTracker tracker, @NotNull Statement statement) {\n    if (statement instanceof BasicBlockStatement  && statement.getExprents() != null) {\n      boolean found = false;\n      for (Exprent statementExprent : statement.getExprents()) {\n        if (collectRecordAssignment(tracker, statementExprent, statement)) {\n          found = true;\n          continue;\n        }\n        return found;\n      }\n    }\n    if (statement instanceof SequenceStatement || statement instanceof IfStatement) {\n      return processAtLeastOneBlock(tracker, statement.getFirst());\n    }\n    return true;\n  }\n\n  /**\n   * Collects record assignment in the given statement into tracker.\n\n   * @return True if a record assignment was found and processed, false otherwise.\n   */\n  private static boolean collectRecordAssignment(@NotNull VarTracker tracker,\n                                                 @Nullable Exprent exprent,\n                                                 @NotNull Statement statement) {\n    if (!(exprent instanceof AssignmentExprent assignmentExprent)) {\n      return false;\n    }\n    Exprent exprentLeft = assignmentExprent.getLeft();\n    if (!(exprentLeft instanceof VarExprent leftVarExprent)) {\n      return false;\n    }\n    Exprent right = assignmentExprent.getRight();\n    if (right instanceof VarExprent varExprent) {\n      RecordVarExprent reassignedRecord = tracker.getRecord(varExprent);\n      if (reassignedRecord == null) {\n        return false;\n      }\n      reassignedRecord.copyFrom(leftVarExprent);\n      tracker.put(leftVarExprent, reassignedRecord, statement);\n    }\n    else if (right instanceof FunctionExprent functionExprent &&\n             functionExprent.getFuncType() == FunctionExprent.FUNCTION_CAST &&\n             functionExprent.getLstOperands().size() == 2 &&\n             functionExprent.getLstOperands().get(1) instanceof ConstExprent constExprent) {\n      Exprent varForCast = functionExprent.getLstOperands().get(0);\n      RecordVarExprent recordToCast = tracker.getRecord(varForCast);\n      if (recordToCast == null) {\n        return false;\n      }\n      if (!recordToCast.getVarType().equals(constExprent.getExprType())) {\n        return false;\n      }\n      recordToCast.copyFrom(leftVarExprent);\n      tracker.put(leftVarExprent, recordToCast, statement);\n    }\n    else {\n      return false;\n    }\n    return true;\n  }\n\n  private static boolean checkRegularEdgesForRecordPattern(@NotNull Statement parent) {\n    for (int i = 0; i < parent.getStats().size(); i++) {\n      Statement statement = parent.getStats().get(i);\n      if (i != parent.getStats().size() - 1) {\n        List<StatEdge> edges = statement.getSuccessorEdges(StatEdge.EdgeType.DIRECT_ALL);\n        if (edges.size() != 1) {\n          return true;\n        }\n        StatEdge edge = edges.get(0);\n        if (edge.getType() != StatEdge.EdgeType.REGULAR || edge.getDestination() != parent.getStats().get(i + 1)) {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n\n  static class PatternVariableCandidate {\n    private final @NotNull VarExprent varExprent;\n    private final @NotNull List<TempVarAssignmentItem> tempVarAssignments;\n    private final @NotNull Runnable cleaner;\n    private final @NotNull Set<IfStatement> usedIfStatement = new HashSet<>();\n    private final @NotNull Statement nextStatement;\n    @Nullable private Exprent guards;\n\n    PatternVariableCandidate(@NotNull VarExprent varExprent,\n                             @NotNull Statement nextStatement,\n                             @NotNull Set<IfStatement> usedIfStatement,\n                             @NotNull List<TempVarAssignmentItem> tempVarAssignments,\n                             @NotNull Runnable cleaner) {\n      this.varExprent = varExprent;\n      this.tempVarAssignments = tempVarAssignments;\n      this.cleaner = cleaner;\n      this.getUsedIfStatement().addAll(usedIfStatement);\n      this.nextStatement = nextStatement;\n    }\n\n    List<TempVarAssignmentItem> getTempAssignments() {\n      return getTempVarAssignments();\n    }\n\n    @NotNull VarExprent getVarExprent() {\n      return varExprent;\n    }\n\n    @NotNull List<TempVarAssignmentItem> getTempVarAssignments() {\n      return tempVarAssignments;\n    }\n\n    @NotNull Runnable getCleaner() {\n      return cleaner;\n    }\n\n    @NotNull Set<IfStatement> getUsedIfStatement() {\n      return usedIfStatement;\n    }\n\n    @NotNull Statement getNextStatement() {\n      return nextStatement;\n    }\n\n    @Nullable Exprent getGuards() {\n      return guards;\n    }\n\n    void setGuards(@Nullable Exprent guards) {\n      this.guards = guards;\n    }\n  }\n\n  static class VarTracker {\n    @NotNull\n    private RecordVarExprent root;\n    @NotNull\n    private final Map<VarExprent, RecordVarExprent> varRecordTracker = new HashMap<>();\n    @NotNull\n    private final List<TempVarAssignmentItem> varTempAssignmentTracker = new ArrayList<>();\n\n    VarTracker(@NotNull RecordVarExprent root) { this.root = root; }\n\n    void put(@NotNull VarExprent varExprent,\n             @NotNull RecordVarExprent recordVarExprent,\n             @Nullable Statement statement) {\n      varRecordTracker.put(varExprent, recordVarExprent);\n      if (statement != null) {\n        varTempAssignmentTracker.add(new TempVarAssignmentItem(varExprent, statement));\n      }\n    }\n\n    @Nullable\n    RecordVarExprent getRecord(@NotNull Exprent exp) {\n      return varRecordTracker.get(exp);\n    }\n\n    @NotNull\n    List<TempVarAssignmentItem> getTempItems() {\n      return varTempAssignmentTracker;\n    }\n\n    @Nullable\n    VarTracker copy() {\n      RecordVarExprent copy = root.copy();\n      Map<RecordVarExprent, RecordVarExprent> mapToNew = new HashMap<>();\n      Queue<Map.Entry<RecordVarExprent, RecordVarExprent>> queue = new ArrayDeque<>();\n      queue.add(Map.entry(root, copy));\n      while (!queue.isEmpty()) {\n        Map.Entry<RecordVarExprent, RecordVarExprent> nextPair = queue.poll();\n        mapToNew.put(nextPair.getKey(), nextPair.getValue());\n        List<RecordVarExprent> oldComponents = nextPair.getKey().getComponents();\n        List<RecordVarExprent> newComponents = nextPair.getValue().getComponents();\n        if (oldComponents.size() != newComponents.size()) {\n          return null;\n        }\n        for (int i = 0; i < oldComponents.size(); i++) {\n          queue.add(Map.entry(oldComponents.get(i), newComponents.get(i)));\n        }\n      }\n      Map<VarExprent, RecordVarExprent> newVarRecordTracker = new HashMap<>();\n      for (Map.Entry<VarExprent, RecordVarExprent> entry : varRecordTracker.entrySet()) {\n        RecordVarExprent newExprent = mapToNew.get(entry.getValue());\n        newVarRecordTracker.put(entry.getKey(), newExprent);\n      }\n      VarTracker newTracker = new VarTracker(copy);\n      newTracker.varRecordTracker.putAll(newVarRecordTracker);\n      newTracker.varTempAssignmentTracker.addAll(varTempAssignmentTracker);\n      return newTracker;\n    }\n\n    private void putAll(@NotNull VarTracker tracker) {\n      root = tracker.root;\n      varRecordTracker.putAll(tracker.varRecordTracker);\n      varTempAssignmentTracker.addAll(tracker.varTempAssignmentTracker);\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExpressionList.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class PrimitiveExpressionList {\n  private final List<Exprent> expressions = new ArrayList<>();\n  private final ExpressionStack stack;\n\n  public PrimitiveExpressionList() {\n    this(new ExpressionStack());\n  }\n\n  private PrimitiveExpressionList(ExpressionStack stack) {\n    this.stack = stack;\n  }\n\n  public PrimitiveExpressionList copy() {\n    return new PrimitiveExpressionList(stack.copy());\n  }\n\n  public List<Exprent> getExpressions() {\n    return expressions;\n  }\n\n  public ExpressionStack getStack() {\n    return stack;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\npublic final class SecondaryFunctionsHelper {\n\n  private static final int[] funcsnot = new int[]{\n    FunctionExprent.FUNCTION_NE,\n    FunctionExprent.FUNCTION_EQ,\n    FunctionExprent.FUNCTION_GE,\n    FunctionExprent.FUNCTION_LT,\n    FunctionExprent.FUNCTION_LE,\n    FunctionExprent.FUNCTION_GT,\n    FunctionExprent.FUNCTION_COR,\n    FunctionExprent.FUNCTION_CADD\n  };\n\n  private static final Map<Integer, Integer[]> mapNumComparisons = Map.of(\n    FunctionExprent.FUNCTION_EQ, new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_GT},\n    FunctionExprent.FUNCTION_NE, new Integer[]{FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_LE},\n    FunctionExprent.FUNCTION_GT, new Integer[]{FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT, null},\n    FunctionExprent.FUNCTION_GE, new Integer[]{null, FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT},\n    FunctionExprent.FUNCTION_LT, new Integer[]{null, FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE},\n    FunctionExprent.FUNCTION_LE, new Integer[]{FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE, null}\n  );\n\n  public static boolean identifySecondaryFunctions(Statement stat, VarProcessor varProc) {\n    if (stat.getExprents() == null) {\n      // if(){;}else{...} -> if(!){...}\n      if (stat.type == StatementType.IF) {\n        IfStatement ifelsestat = (IfStatement)stat;\n        Statement ifstat = ifelsestat.getIfstat();\n\n        if (ifelsestat.iftype == IfStatement.IFTYPE_IFELSE && ifstat.getExprents() != null &&\n            ifstat.getExprents().isEmpty() && (ifstat.getAllSuccessorEdges().isEmpty() || !ifstat.getAllSuccessorEdges().get(0).explicit)) {\n\n          // move else to the if position\n          ifelsestat.getStats().removeWithKey(ifstat.id);\n\n          ifelsestat.iftype = IfStatement.IFTYPE_IF;\n          ifelsestat.setIfstat(ifelsestat.getElsestat());\n          ifelsestat.setElsestat(null);\n\n          if (ifelsestat.getAllSuccessorEdges().isEmpty() && !ifstat.getAllSuccessorEdges().isEmpty()) {\n            StatEdge endedge = ifstat.getAllSuccessorEdges().get(0);\n\n            ifstat.removeSuccessor(endedge);\n            endedge.setSource(ifelsestat);\n            if (endedge.closure != null) {\n              ifelsestat.getParent().addLabeledEdge(endedge);\n            }\n            ifelsestat.addSuccessor(endedge);\n          }\n\n          ifelsestat.getFirst().removeSuccessor(ifelsestat.getIfEdge());\n\n          ifelsestat.setIfEdge(ifelsestat.getElseEdge());\n          ifelsestat.setElseEdge(null);\n\n          // negate head expression\n          ifelsestat.setNegated(!ifelsestat.isNegated());\n          ifelsestat.getHeadexprentList().set(0, ((IfExprent)ifelsestat.getHeadexprent().copy()).negateIf());\n\n          return true;\n        }\n      }\n    }\n\n    boolean replaced = true;\n    while (replaced) {\n      replaced = false;\n\n      List<Object> lstObjects = new ArrayList<>(stat.getExprents() == null ? stat.getSequentialObjects() : stat.getExprents());\n\n      for (int i = 0; i < lstObjects.size(); i++) {\n        Object obj = lstObjects.get(i);\n\n        if (obj instanceof Statement) {\n          if (identifySecondaryFunctions((Statement)obj, varProc)) {\n            replaced = true;\n            break;\n          }\n        }\n        else if (obj instanceof Exprent) {\n          Exprent retexpr = identifySecondaryFunctions((Exprent)obj, true, varProc);\n          if (retexpr != null) {\n            if (stat.getExprents() == null) {\n              // only head expressions can be replaced!\n              stat.replaceExprent((Exprent)obj, retexpr);\n            }\n            else {\n              stat.getExprents().set(i, retexpr);\n            }\n            replaced = true;\n            break;\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static Exprent identifySecondaryFunctions(Exprent exprent, boolean statement_level, VarProcessor varProc) {\n    if (exprent.type == Exprent.EXPRENT_FUNCTION) {\n      FunctionExprent fexpr = (FunctionExprent)exprent;\n\n      switch (fexpr.getFuncType()) {\n        case FunctionExprent.FUNCTION_BOOL_NOT -> {\n\n          Exprent retparam = propagateBoolNot(fexpr);\n\n          if (retparam != null) {\n            return retparam;\n          }\n        }\n        case FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_NE, FunctionExprent.FUNCTION_GT,\n          FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_LE -> {\n          Exprent expr1 = fexpr.getLstOperands().get(0);\n          Exprent expr2 = fexpr.getLstOperands().get(1);\n\n          if (expr1.type == Exprent.EXPRENT_CONST) {\n            expr2 = expr1;\n            expr1 = fexpr.getLstOperands().get(1);\n          }\n\n          if (expr1.type == Exprent.EXPRENT_FUNCTION && expr2.type == Exprent.EXPRENT_CONST) {\n            FunctionExprent funcexpr = (FunctionExprent)expr1;\n            ConstExprent cexpr = (ConstExprent)expr2;\n\n            int functype = funcexpr.getFuncType();\n            if (functype == FunctionExprent.FUNCTION_LCMP || functype == FunctionExprent.FUNCTION_FCMPG ||\n                functype == FunctionExprent.FUNCTION_FCMPL || functype == FunctionExprent.FUNCTION_DCMPG ||\n                functype == FunctionExprent.FUNCTION_DCMPL) {\n\n              int desttype = -1;\n\n              Integer[] destcons = mapNumComparisons.get(fexpr.getFuncType());\n              if (destcons != null) {\n                int index = cexpr.getIntValue() + 1;\n                if (index >= 0 && index <= 2) {\n                  Integer destcon = destcons[index];\n                  if (destcon != null) {\n                    desttype = destcon;\n                  }\n                }\n              }\n\n              if (desttype >= 0) {\n                if (functype != FunctionExprent.FUNCTION_LCMP) {\n                  boolean oneForNan = functype == FunctionExprent.FUNCTION_DCMPL || functype == FunctionExprent.FUNCTION_FCMPL;\n                  boolean trueForOne = desttype == FunctionExprent.FUNCTION_LT || desttype == FunctionExprent.FUNCTION_LE;\n                  boolean trueForNan = oneForNan == trueForOne;\n                  if (trueForNan) {\n                    List<Exprent> operands = new ArrayList<>();\n                    operands.add(new FunctionExprent(funcsnot[desttype - FunctionExprent.FUNCTION_EQ],\n                                                     funcexpr.getLstOperands(), funcexpr.bytecode));\n                    return new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, operands, funcexpr.bytecode);\n                  }\n                }\n                return new FunctionExprent(desttype, funcexpr.getLstOperands(), funcexpr.bytecode);\n              }\n            }\n          }\n        }\n      }\n    }\n\n\n    boolean replaced = true;\n    while (replaced) {\n      replaced = false;\n\n      for (Exprent expr : exprent.getAllExprents()) {\n        Exprent retexpr = identifySecondaryFunctions(expr, false, varProc);\n        if (retexpr != null) {\n          exprent.replaceExprent(expr, retexpr);\n          replaced = true;\n          break;\n        }\n      }\n    }\n\n    switch (exprent.type) {\n      case Exprent.EXPRENT_FUNCTION -> {\n        FunctionExprent fexpr = (FunctionExprent)exprent;\n        List<Exprent> lstOperands = fexpr.getLstOperands();\n\n        switch (fexpr.getFuncType()) {\n          case FunctionExprent.FUNCTION_XOR -> {\n            for (int i = 0; i < 2; i++) {\n              Exprent operand = lstOperands.get(i);\n              VarType operandtype = operand.getExprType();\n\n              if (operand.type == Exprent.EXPRENT_CONST &&\n                  operandtype.getType() != CodeConstants.TYPE_BOOLEAN) {\n                ConstExprent cexpr = (ConstExprent)operand;\n                long val;\n                if (operandtype.getType() == CodeConstants.TYPE_LONG) {\n                  val = (Long)cexpr.getValue();\n                }\n                else {\n                  val = (Integer)cexpr.getValue();\n                }\n\n                if (val == -1) {\n                  List<Exprent> lstBitNotOperand = new ArrayList<>();\n                  lstBitNotOperand.add(lstOperands.get(1 - i));\n                  return new FunctionExprent(FunctionExprent.FUNCTION_BIT_NOT, lstBitNotOperand, fexpr.bytecode);\n                }\n              }\n            }\n          }\n          case FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_NE -> {\n            if (lstOperands.get(0).getExprType().getType() == CodeConstants.TYPE_BOOLEAN &&\n                lstOperands.get(1).getExprType().getType() == CodeConstants.TYPE_BOOLEAN) {\n              for (int i = 0; i < 2; i++) {\n                if (lstOperands.get(i).type == Exprent.EXPRENT_CONST) {\n                  ConstExprent cexpr = (ConstExprent)lstOperands.get(i);\n                  int val = (Integer)cexpr.getValue();\n\n                  if ((fexpr.getFuncType() == FunctionExprent.FUNCTION_EQ && val == 1) ||\n                      (fexpr.getFuncType() == FunctionExprent.FUNCTION_NE && val == 0)) {\n                    return lstOperands.get(1 - i);\n                  }\n                  else {\n                    List<Exprent> lstNotOperand = new ArrayList<>();\n                    lstNotOperand.add(lstOperands.get(1 - i));\n                    return new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, lstNotOperand, fexpr.bytecode);\n                  }\n                }\n              }\n            }\n          }\n          case FunctionExprent.FUNCTION_BOOL_NOT -> {\n            if (lstOperands.get(0).type == Exprent.EXPRENT_CONST) {\n              int val = ((ConstExprent)lstOperands.get(0)).getIntValue();\n              if (val == 0) {\n                return new ConstExprent(VarType.VARTYPE_BOOLEAN, 1, fexpr.bytecode);\n              }\n              else {\n                return new ConstExprent(VarType.VARTYPE_BOOLEAN, 0, fexpr.bytecode);\n              }\n            }\n          }\n          case FunctionExprent.FUNCTION_IIF -> {\n            Exprent expr1 = lstOperands.get(1);\n            Exprent expr2 = lstOperands.get(2);\n\n            if (expr1.type == Exprent.EXPRENT_CONST && expr2.type == Exprent.EXPRENT_CONST) {\n              ConstExprent cexpr1 = (ConstExprent)expr1;\n              ConstExprent cexpr2 = (ConstExprent)expr2;\n\n              if (cexpr1.getExprType().getType() == CodeConstants.TYPE_BOOLEAN &&\n                  cexpr2.getExprType().getType() == CodeConstants.TYPE_BOOLEAN) {\n\n                if (cexpr1.getIntValue() == 0 && cexpr2.getIntValue() != 0) {\n                  return new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, lstOperands.get(0), fexpr.bytecode);\n                }\n                else if (cexpr1.getIntValue() != 0 && cexpr2.getIntValue() == 0) {\n                  return lstOperands.get(0);\n                }\n              }\n            }\n          }\n          case FunctionExprent.FUNCTION_LCMP, FunctionExprent.FUNCTION_FCMPL, FunctionExprent.FUNCTION_FCMPG, FunctionExprent.FUNCTION_DCMPL, FunctionExprent.FUNCTION_DCMPG -> {\n            int var = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER);\n            VarType type = lstOperands.get(0).getExprType();\n\n            FunctionExprent iff = new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(\n              new FunctionExprent(FunctionExprent.FUNCTION_LT, Arrays.asList(new VarExprent(var, type, varProc),\n                                                                             ConstExprent.getZeroConstant(type.getType())), null),\n              new ConstExprent(VarType.VARTYPE_INT, -1, null),\n              new ConstExprent(VarType.VARTYPE_INT, 1, null)), null);\n\n            FunctionExprent head = new FunctionExprent(FunctionExprent.FUNCTION_EQ, Arrays.asList(\n              new AssignmentExprent(new VarExprent(var, type, varProc),\n                                    new FunctionExprent(FunctionExprent.FUNCTION_SUB, Arrays.asList(lstOperands.get(0), lstOperands.get(1)),\n                                                        null),\n                                    null),\n              ConstExprent.getZeroConstant(type.getType())), null);\n\n            varProc.setVarType(new VarVersionPair(var, 0), type);\n\n            return new FunctionExprent(FunctionExprent.FUNCTION_IIF, Arrays.asList(\n              head, new ConstExprent(VarType.VARTYPE_INT, 0, null), iff), fexpr.bytecode);\n          }\n        }\n      }\n      case Exprent.EXPRENT_ASSIGNMENT -> { // check for conditional assignment\n        AssignmentExprent asexpr = (AssignmentExprent)exprent;\n        Exprent right = asexpr.getRight();\n        Exprent left = asexpr.getLeft();\n\n        if (right.type == Exprent.EXPRENT_FUNCTION) {\n          FunctionExprent func = (FunctionExprent)right;\n\n          VarType midlayer = null;\n          if (func.getFuncType() >= FunctionExprent.FUNCTION_I2L &&\n              func.getFuncType() <= FunctionExprent.FUNCTION_I2S) {\n            right = func.getLstOperands().get(0);\n            midlayer = func.getSimpleCastType();\n            if (right.type == Exprent.EXPRENT_FUNCTION) {\n              func = (FunctionExprent)right;\n            }\n            else {\n              return null;\n            }\n          }\n\n          List<Exprent> lstFuncOperands = func.getLstOperands();\n\n          Exprent cond = null;\n\n          switch (func.getFuncType()) {\n            case FunctionExprent.FUNCTION_ADD:\n            case FunctionExprent.FUNCTION_AND:\n            case FunctionExprent.FUNCTION_OR:\n            case FunctionExprent.FUNCTION_XOR:\n              if (left.equals(lstFuncOperands.get(1))) {\n                cond = lstFuncOperands.get(0);\n                break;\n              }\n            case FunctionExprent.FUNCTION_SUB:\n            case FunctionExprent.FUNCTION_MUL:\n            case FunctionExprent.FUNCTION_DIV:\n            case FunctionExprent.FUNCTION_REM:\n            case FunctionExprent.FUNCTION_SHL:\n            case FunctionExprent.FUNCTION_SHR:\n            case FunctionExprent.FUNCTION_USHR:\n              if (left.equals(lstFuncOperands.get(0))) {\n                cond = lstFuncOperands.get(1);\n              }\n          }\n\n          if (cond != null && (midlayer == null || midlayer.equals(cond.getExprType()))) {\n            asexpr.setRight(cond);\n            asexpr.setCondType(func.getFuncType());\n          }\n        }\n      }\n      case Exprent.EXPRENT_INVOCATION -> {\n        if (!statement_level) { // simplify if exprent is a real expression. The opposite case is pretty absurd, can still happen however (and happened at least once).\n          Exprent retexpr = ConcatenationHelper.contractStringConcat(exprent);\n          if (!exprent.equals(retexpr)) {\n            return retexpr;\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n\n  public static Exprent propagateBoolNot(Exprent exprent) {\n    if (exprent.type == Exprent.EXPRENT_FUNCTION) {\n      FunctionExprent fexpr = (FunctionExprent)exprent;\n\n      if (fexpr.getFuncType() == FunctionExprent.FUNCTION_BOOL_NOT) {\n\n        Exprent param = fexpr.getLstOperands().get(0);\n\n        if (param.type == Exprent.EXPRENT_FUNCTION) {\n          FunctionExprent fparam = (FunctionExprent)param;\n\n          int ftype = fparam.getFuncType();\n          boolean canSimplify = false;\n          switch (ftype) {\n            case FunctionExprent.FUNCTION_BOOL_NOT:\n              Exprent newexpr = fparam.getLstOperands().get(0);\n              Exprent retexpr = propagateBoolNot(newexpr);\n              return retexpr == null ? newexpr : retexpr;\n            case FunctionExprent.FUNCTION_CADD, FunctionExprent.FUNCTION_COR: {\n              List<Exprent> operands = fparam.getLstOperands();\n              for (int i = 0; i < operands.size(); i++) {\n                Exprent newparam = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, operands.get(i), operands.get(i).bytecode);\n\n                Exprent retparam = propagateBoolNot(newparam);\n                operands.set(i, retparam == null ? newparam : retparam);\n              }\n            }\n            case FunctionExprent.FUNCTION_EQ, FunctionExprent.FUNCTION_NE:\n              canSimplify = true;\n            case FunctionExprent.FUNCTION_LT, FunctionExprent.FUNCTION_GE, FunctionExprent.FUNCTION_GT, FunctionExprent.FUNCTION_LE:\n              if (!canSimplify) {\n                List<Exprent> operands = fparam.getLstOperands();\n                VarType left = operands.get(0).getExprType();\n                VarType right = operands.get(1).getExprType();\n                VarType commonSupertype = VarType.getCommonSupertype(left, right);\n                if (commonSupertype != null) {\n                  canSimplify = commonSupertype.getType() != CodeConstants.TYPE_FLOAT && commonSupertype.getType() != CodeConstants.TYPE_DOUBLE;\n                }\n              }\n              if (canSimplify) {\n                fparam.setFuncType(funcsnot[ftype - FunctionExprent.FUNCTION_EQ]);\n                return fparam;\n              }\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\n\n\npublic final class SequenceHelper {\n\n\n  public static void condenseSequences(Statement root) {\n    condenseSequencesRec(root);\n  }\n\n  private static void condenseSequencesRec(Statement stat) {\n\n    if (stat.type == StatementType.SEQUENCE) {\n\n      List<Statement> lst = new ArrayList<>(stat.getStats());\n\n      boolean unfolded = false;\n\n      // unfold blocks\n      for (int i = 0; i < lst.size(); i++) {\n        Statement st = lst.get(i);\n        if (st.type == StatementType.SEQUENCE) {\n\n          removeEmptyStatements((SequenceStatement)st);\n\n          if (i == lst.size() - 1 || isSequenceDisbandable(st, lst.get(i + 1))) {\n            // move predecessors\n            Statement first = st.getFirst();\n            for (StatEdge edge : st.getAllPredecessorEdges()) {\n              st.removePredecessor(edge);\n              edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, first);\n              first.addPredecessor(edge);\n            }\n\n            // move successors\n            Statement last = st.getStats().getLast();\n            if (last.getAllSuccessorEdges().isEmpty() && i < lst.size() - 1) {\n              last.addSuccessor(new StatEdge(EdgeType.REGULAR, last, lst.get(i + 1)));\n            }\n            else {\n              for (StatEdge edge : last.getAllSuccessorEdges()) {\n                if (i == lst.size() - 1) {\n                  if (edge.closure == st) {\n                    stat.addLabeledEdge(edge);\n                  }\n                }\n                else {\n                  edge.getSource().changeEdgeType(EdgeDirection.FORWARD, edge, EdgeType.REGULAR);\n                  edge.closure.getLabelEdges().remove(edge);\n                  edge.closure = null;\n                }\n              }\n            }\n\n            for (StatEdge edge : st.getAllSuccessorEdges()) {\n              st.removeSuccessor(edge);\n            }\n\n            for (StatEdge edge : new HashSet<>(st.getLabelEdges())) {\n              if (edge.getSource() != last) {\n                last.addLabeledEdge(edge);\n              }\n            }\n\n            lst.remove(i);\n            lst.addAll(i, st.getStats());\n            i--;\n\n            unfolded = true;\n          }\n        }\n      }\n\n      if (unfolded) {\n        SequenceStatement sequence = new SequenceStatement(lst);\n        sequence.setAllParent();\n\n        stat.getParent().replaceStatement(stat, sequence);\n\n        stat = sequence;\n      }\n    }\n\n    // sequence consisting of one statement -> disband\n    if (stat.type == StatementType.SEQUENCE) {\n\n      removeEmptyStatements((SequenceStatement)stat);\n\n      if (stat.getStats().size() == 1) {\n\n        Statement st = stat.getFirst();\n\n        boolean ok = st.getAllSuccessorEdges().isEmpty();\n        if (!ok) {\n          StatEdge edge = st.getAllSuccessorEdges().get(0);\n\n          ok = stat.getAllSuccessorEdges().isEmpty();\n          if (!ok) {\n            StatEdge statedge = stat.getAllSuccessorEdges().get(0);\n            ok = (edge.getDestination() == statedge.getDestination());\n\n            if (ok) {\n              st.removeSuccessor(edge);\n            }\n          }\n        }\n\n        if (ok) {\n          stat.getParent().replaceStatement(stat, st);\n          stat = st;\n        }\n      }\n    }\n\n    // replace flat statements with synthetic basic blocks\n    outer:\n    while (true) {\n      for (Statement st : stat.getStats()) {\n        if ((st.getStats().isEmpty() || st.getExprents() != null) && st.type != StatementType.BASIC_BLOCK) {\n          destroyAndFlattenStatement(st);\n          continue outer;\n        }\n      }\n      break;\n    }\n\n    // recursion\n    for (int i = 0; i < stat.getStats().size(); i++) {\n      condenseSequencesRec(stat.getStats().get(i));\n    }\n  }\n\n  private static boolean isSequenceDisbandable(Statement block, Statement next) {\n\n    Statement last = block.getStats().getLast();\n    List<StatEdge> lstSuccs = last.getAllSuccessorEdges();\n    if (!lstSuccs.isEmpty()) {\n      if (lstSuccs.get(0).getDestination() != next) {\n        return false;\n      }\n    }\n\n    for (StatEdge edge : next.getPredecessorEdges(EdgeType.BREAK)) {\n      if (last != edge.getSource() && !last.containsStatementStrict(edge.getSource())) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  private static void removeEmptyStatements(SequenceStatement sequence) {\n\n    if (sequence.getStats().size() <= 1) {\n      return;\n    }\n\n    mergeFlatStatements(sequence);\n\n    while (true) {\n\n      boolean found = false;\n\n      for (Statement st : sequence.getStats()) {\n\n        if (st.getExprents() != null && st.getExprents().isEmpty()) {\n\n          if (st.getAllSuccessorEdges().isEmpty()) {\n            List<StatEdge> lstBreaks = st.getPredecessorEdges(EdgeType.BREAK);\n\n            if (lstBreaks.isEmpty()) {\n              for (StatEdge edge : st.getAllPredecessorEdges()) {\n                edge.getSource().removeSuccessor(edge);\n              }\n              found = true;\n            }\n          }\n          else {\n            StatEdge sucedge = st.getAllSuccessorEdges().get(0);\n            if (sucedge.getType() != EdgeType.FINALLY_EXIT) {\n              st.removeSuccessor(sucedge);\n\n              for (StatEdge edge : st.getAllPredecessorEdges()) {\n                if (sucedge.getType() != EdgeType.REGULAR) {\n                  edge.getSource().changeEdgeType(EdgeDirection.FORWARD, edge, sucedge.getType());\n                }\n\n                st.removePredecessor(edge);\n                edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, sucedge.getDestination());\n                sucedge.getDestination().addPredecessor(edge);\n\n                if (sucedge.closure != null) {\n                  sucedge.closure.addLabeledEdge(edge);\n                }\n              }\n              found = true;\n            }\n          }\n\n          if (found) {\n            sequence.getStats().removeWithKey(st.id);\n            break;\n          }\n        }\n      }\n\n      if (!found) {\n        break;\n      }\n    }\n\n    sequence.setFirst(sequence.getStats().get(0));\n  }\n\n  private static void mergeFlatStatements(SequenceStatement sequence) {\n\n    while (true) {\n\n      Statement next;\n      Statement current = null;\n\n      boolean found = false;\n\n      for (int i = sequence.getStats().size() - 1; i >= 0; i--) {\n\n        next = current;\n        current = sequence.getStats().get(i);\n\n        if (next != null && current.getExprents() != null && !current.getExprents().isEmpty()) {\n          if (next.getExprents() != null) {\n            next.getExprents().addAll(0, current.getExprents());\n            current.getExprents().clear();\n            found = true;\n          }\n          else {\n            Statement first = getFirstExprentlist(next);\n            if (first != null) {\n              first.getExprents().addAll(0, current.getExprents());\n              current.getExprents().clear();\n              found = true;\n            }\n          }\n        }\n      }\n\n      if (!found) {\n        break;\n      }\n    }\n  }\n\n  private static Statement getFirstExprentlist(Statement stat) {\n\n    if (stat.getExprents() != null) {\n      return stat;\n    }\n\n    return switch (stat.type) {\n      case IF, SEQUENCE, SWITCH, SYNCHRONIZED -> getFirstExprentlist(stat.getFirst());\n      default -> null;\n    };\n  }\n\n\n  public static void destroyAndFlattenStatement(Statement stat) {\n\n    destroyStatementContent(stat, false);\n\n    BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(\n      DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));\n    if (stat.getExprents() == null) {\n      bstat.setExprents(new ArrayList<>());\n    }\n    else {\n      bstat.setExprents(DecHelper.copyExprentList(stat.getExprents()));\n    }\n\n    stat.getParent().replaceStatement(stat, bstat);\n  }\n\n  public static void destroyStatementContent(Statement stat, boolean self) {\n\n    for (Statement st : stat.getStats()) {\n      destroyStatementContent(st, true);\n    }\n    stat.getStats().clear();\n\n    if (self) {\n      for (StatEdge edge : stat.getAllSuccessorEdges()) {\n        stat.removeSuccessor(edge);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class SimplifyExprentsHelper {\n  @SuppressWarnings(\"SpellCheckingInspection\") private static final MatchEngine class14Builder = new MatchEngine(\n    \"\"\"\n      statement type:if iftype:if exprsize:-1\n       exprent position:head type:if\n        exprent type:function functype:eq\n         exprent type:field name:$fieldname$\n         exprent type:constant consttype:null\n       statement type:basicblock\n        exprent position:-1 type:assignment ret:$assignfield$\n         exprent type:var index:$var$\n         exprent type:field name:$fieldname$\n       statement type:sequence statsize:2\n        statement type:trycatch\n         statement type:basicblock exprsize:1\n          exprent type:assignment\n           exprent type:var index:$var$\n           exprent type:invocation invclass:java/lang/Class signature:forName(Ljava/lang/String;)Ljava/lang/Class;\n            exprent position:0 type:constant consttype:string constvalue:$classname$\n         statement type:basicblock exprsize:1\n          exprent type:exit exittype:throw\n        statement type:basicblock exprsize:1\n         exprent type:assignment\n          exprent type:field name:$fieldname$ ret:$field$\n          exprent type:var index:$var$\"\"\");\n\n  private final boolean firstInvocation;\n\n  public SimplifyExprentsHelper(boolean firstInvocation) {\n    this.firstInvocation = firstInvocation;\n  }\n\n  public boolean simplifyStackVarsStatement(Statement stat, Set<Integer> setReorderedIfs, SSAConstructorSparseEx ssa, StructClass cl) {\n    boolean res = false;\n\n    List<Exprent> expressions = stat.getExprents();\n    if (expressions == null) {\n      boolean processClass14 = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4);\n\n      while (true) {\n        boolean changed = false;\n\n        for (Statement st : stat.getStats()) {\n          res |= simplifyStackVarsStatement(st, setReorderedIfs, ssa, cl);\n\n          changed = IfHelper.mergeIfs(st, setReorderedIfs) ||  // collapse composed if's\n                    buildIff(st, ssa) ||  // collapse iff ?: statement\n                    processClass14 && collapseInlinedClass14(st);  // collapse inlined .class property in version 1.4 and before\n\n          if (changed) {\n            break;\n          }\n        }\n\n        res |= changed;\n\n        if (!changed) {\n          break;\n        }\n      }\n    }\n    else {\n      res = simplifyStackVarsExprents(expressions, cl);\n    }\n\n    return res;\n  }\n\n  private boolean simplifyStackVarsExprents(List<Exprent> list, StructClass cl) {\n    boolean res = false;\n\n    int index = 0;\n    while (index < list.size()) {\n      Exprent current = list.get(index);\n\n      Exprent ret = isSimpleConstructorInvocation(current);\n      if (ret != null) {\n        list.set(index, ret);\n        res = true;\n        continue;\n      }\n\n      // lambda expression (Java 8)\n      ret = isLambda(current, cl);\n      if (ret != null) {\n        list.set(index, ret);\n        res = true;\n        continue;\n      }\n\n      // remove monitor exit\n      if (isMonitorExit(current)) {\n        list.remove(index);\n        res = true;\n        continue;\n      }\n\n      // trivial assignment of a stack variable\n      if (isTrivialStackAssignment(current)) {\n        list.remove(index);\n        res = true;\n        continue;\n      }\n\n      if (index == list.size() - 1) {\n        break;\n      }\n\n      Exprent next = list.get(index + 1);\n\n      // constructor invocation\n      if (isConstructorInvocationRemote(list, index)) {\n        list.remove(index);\n        res = true;\n        continue;\n      }\n\n      // remove getClass() invocation, which is part of a qualified new\n      if (DecompilerContext.getOption(IFernflowerPreferences.REMOVE_GET_CLASS_NEW)) {\n        if (isQualifiedNewGetClass(current, next)) {\n          list.remove(index);\n          res = true;\n          continue;\n        }\n      }\n\n      // direct initialization of an array\n      int arrCount = isArrayInitializer(list, index);\n      if (arrCount > 0) {\n        for (int i = 0; i < arrCount; i++) {\n          list.remove(index + 1);\n        }\n        res = true;\n        continue;\n      }\n\n      // add array initializer expression\n      if (addArrayInitializer(current, next)) {\n        list.remove(index + 1);\n        res = true;\n        continue;\n      }\n\n      // integer ++expr and --expr  (except for vars!)\n      Exprent func = isPPIorMMI(current);\n      if (func != null) {\n        list.set(index, func);\n        res = true;\n        continue;\n      }\n\n      // expr++ and expr--\n      if (isIPPorIMM(current, next) || isIPPorIMM2(current, next)) {\n        list.remove(index + 1);\n        res = true;\n        continue;\n      }\n\n      // assignment on stack\n      if (isStackAssignment(current, next)) {\n        list.remove(index + 1);\n        res = true;\n        continue;\n      }\n\n      if (!firstInvocation && isStackAssignment2(current, next)) {\n        list.remove(index + 1);\n        res = true;\n        continue;\n      }\n\n      index++;\n    }\n\n    return res;\n  }\n\n  private static boolean addArrayInitializer(Exprent first, Exprent second) {\n    if (first.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent as = (AssignmentExprent)first;\n\n      if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) {\n        NewExprent newExpr = (NewExprent)as.getRight();\n\n        if (!newExpr.getLstArrayElements().isEmpty()) {\n          VarExprent arrVar = (VarExprent)as.getLeft();\n\n          if (second.type == Exprent.EXPRENT_ASSIGNMENT) {\n            AssignmentExprent aas = (AssignmentExprent)second;\n            if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) {\n              ArrayExprent arrExpr = (ArrayExprent)aas.getLeft();\n              if (arrExpr.getArray().type == Exprent.EXPRENT_VAR &&\n                  arrVar.equals(arrExpr.getArray()) &&\n                  arrExpr.getIndex().type == Exprent.EXPRENT_CONST) {\n                int constValue = ((ConstExprent)arrExpr.getIndex()).getIntValue();\n\n                if (constValue < newExpr.getLstArrayElements().size()) {\n                  Exprent init = newExpr.getLstArrayElements().get(constValue);\n                  if (init.type == Exprent.EXPRENT_CONST) {\n                    ConstExprent cinit = (ConstExprent)init;\n                    VarType arrType = newExpr.getNewType().decreaseArrayDim();\n                    ConstExprent defaultVal = ExprProcessor.getDefaultArrayValue(arrType);\n\n                    if (cinit.equals(defaultVal)) {\n                      Exprent tempExpr = aas.getRight();\n\n                      if (!tempExpr.containsExprent(arrVar)) {\n                        newExpr.getLstArrayElements().set(constValue, tempExpr);\n\n                        if (tempExpr.type == Exprent.EXPRENT_NEW) {\n                          NewExprent tempNewExpr = (NewExprent)tempExpr;\n                          int dims = newExpr.getNewType().getArrayDim();\n                          if (dims > 1 && !tempNewExpr.getLstArrayElements().isEmpty()) {\n                            tempNewExpr.setDirectArrayInit(true);\n                          }\n                        }\n\n                        return true;\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static int isArrayInitializer(List<Exprent> list, int index) {\n    Exprent current = list.get(index);\n    if (current.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent as = (AssignmentExprent)current;\n\n      if (as.getRight().type == Exprent.EXPRENT_NEW && as.getLeft().type == Exprent.EXPRENT_VAR) {\n        NewExprent newExpr = (NewExprent)as.getRight();\n\n        if (newExpr.getExprType().getArrayDim() > 0 && newExpr.getLstDims().size() == 1 && newExpr.getLstArrayElements().isEmpty() &&\n            newExpr.getLstDims().get(0).type == Exprent.EXPRENT_CONST) {\n\n          int size = (Integer)((ConstExprent)newExpr.getLstDims().get(0)).getValue();\n          if (size == 0) {\n            return 0;\n          }\n\n          VarExprent arrVar = (VarExprent)as.getLeft();\n          Map<Integer, Exprent> mapInit = new HashMap<>();\n\n          int i = 1;\n          while (index + i < list.size() && i <= size) {\n            boolean found = false;\n\n            Exprent expr = list.get(index + i);\n            if (expr.type == Exprent.EXPRENT_ASSIGNMENT) {\n              AssignmentExprent aas = (AssignmentExprent)expr;\n              if (aas.getLeft().type == Exprent.EXPRENT_ARRAY) {\n                ArrayExprent arrExpr = (ArrayExprent)aas.getLeft();\n                if (arrExpr.getArray().type == Exprent.EXPRENT_VAR && arrVar.equals(arrExpr.getArray()) &&\n                    arrExpr.getIndex().type == Exprent.EXPRENT_CONST) {\n                  // TODO: check for a number type. Failure extremely improbable, but nevertheless...\n                  int constValue = ((ConstExprent)arrExpr.getIndex()).getIntValue();\n                  if (constValue < size && !mapInit.containsKey(constValue)) {\n                    if (!aas.getRight().containsExprent(arrVar)) {\n                      mapInit.put(constValue, aas.getRight());\n                      found = true;\n                    }\n                  }\n                }\n              }\n            }\n\n            if (!found) {\n              break;\n            }\n\n            i++;\n          }\n\n          double fraction = ((double)mapInit.size()) / size;\n\n          if ((arrVar.isStack() && fraction > 0) || (size <= 7 && fraction >= 0.3) || (size > 7 && fraction >= 0.7)) {\n            List<Exprent> lstRet = new ArrayList<>();\n\n            VarType arrayType = newExpr.getNewType().decreaseArrayDim();\n            ConstExprent defaultVal = ExprProcessor.getDefaultArrayValue(arrayType);\n            for (int j = 0; j < size; j++) {\n              lstRet.add(defaultVal.copy());\n            }\n\n            int dims = newExpr.getNewType().getArrayDim();\n            for (Entry<Integer, Exprent> ent : mapInit.entrySet()) {\n              Exprent tempExpr = ent.getValue();\n              lstRet.set(ent.getKey(), tempExpr);\n\n              if (tempExpr.type == Exprent.EXPRENT_NEW) {\n                NewExprent tempNewExpr = (NewExprent)tempExpr;\n                if (dims > 1 && !tempNewExpr.getLstArrayElements().isEmpty()) {\n                  tempNewExpr.setDirectArrayInit(true);\n                }\n              }\n            }\n\n            newExpr.setLstArrayElements(lstRet);\n\n            return mapInit.size();\n          }\n        }\n      }\n    }\n\n    return 0;\n  }\n\n  private static boolean isTrivialStackAssignment(Exprent first) {\n    if (first.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent asf = (AssignmentExprent)first;\n\n      if (asf.getLeft().type == Exprent.EXPRENT_VAR && asf.getRight().type == Exprent.EXPRENT_VAR) {\n        VarExprent left = (VarExprent)asf.getLeft();\n        VarExprent right = (VarExprent)asf.getRight();\n        return left.getIndex() == right.getIndex() && left.isStack() && right.isStack();\n      }\n    }\n\n    return false;\n  }\n\n  private static boolean isStackAssignment2(Exprent first, Exprent second) {  // e.g. 1.4-style class invocation\n    if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent asf = (AssignmentExprent)first;\n      AssignmentExprent ass = (AssignmentExprent)second;\n\n      if (asf.getLeft().type == Exprent.EXPRENT_VAR && ass.getRight().type == Exprent.EXPRENT_VAR &&\n          asf.getLeft().equals(ass.getRight()) && ((VarExprent)asf.getLeft()).isStack()) {\n        if (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack()) {\n          asf.setRight(new AssignmentExprent(ass.getLeft(), asf.getRight(), ass.bytecode));\n          return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static boolean isStackAssignment(Exprent first, Exprent second) {\n    if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent asf = (AssignmentExprent)first;\n      AssignmentExprent ass = (AssignmentExprent)second;\n\n      while (true) {\n        if (asf.getRight().equals(ass.getRight())) {\n          if ((asf.getLeft().type == Exprent.EXPRENT_VAR && ((VarExprent)asf.getLeft()).isStack()) &&\n              (ass.getLeft().type != Exprent.EXPRENT_VAR || !((VarExprent)ass.getLeft()).isStack())) {\n\n            if (!ass.getLeft().containsExprent(asf.getLeft())) {\n              asf.setRight(ass);\n              return true;\n            }\n          }\n        }\n        if (asf.getRight().type == Exprent.EXPRENT_ASSIGNMENT) {\n          asf = (AssignmentExprent)asf.getRight();\n        }\n        else {\n          break;\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static Exprent isPPIorMMI(Exprent first) {\n    if (first.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent as = (AssignmentExprent)first;\n\n      if (as.getRight().type == Exprent.EXPRENT_FUNCTION) {\n        FunctionExprent func = (FunctionExprent)as.getRight();\n\n        if (func.getFuncType() == FunctionExprent.FUNCTION_ADD || func.getFuncType() == FunctionExprent.FUNCTION_SUB) {\n          Exprent econd = func.getLstOperands().get(0);\n          Exprent econst = func.getLstOperands().get(1);\n\n          if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST &&\n              func.getFuncType() == FunctionExprent.FUNCTION_ADD) {\n            econd = econst;\n            econst = func.getLstOperands().get(0);\n          }\n\n          if (econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) {\n            Exprent left = as.getLeft();\n\n            if (left.type != Exprent.EXPRENT_VAR && left.equals(econd)) {\n              int type = func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_PPI : FunctionExprent.FUNCTION_MMI;\n              FunctionExprent ret = new FunctionExprent(type, econd, func.bytecode);\n              ret.setImplicitType(VarType.VARTYPE_INT);\n              return ret;\n            }\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n\n  private static boolean isIPPorIMM(Exprent first, Exprent second) {\n    if (first.type == Exprent.EXPRENT_ASSIGNMENT && second.type == Exprent.EXPRENT_FUNCTION) {\n      AssignmentExprent as = (AssignmentExprent)first;\n      FunctionExprent in = (FunctionExprent)second;\n\n      if ((in.getFuncType() == FunctionExprent.FUNCTION_MMI || in.getFuncType() == FunctionExprent.FUNCTION_PPI) &&\n          in.getLstOperands().get(0).equals(as.getRight())) {\n\n        if (in.getFuncType() == FunctionExprent.FUNCTION_MMI) {\n          in.setFuncType(FunctionExprent.FUNCTION_IMM);\n        }\n        else {\n          in.setFuncType(FunctionExprent.FUNCTION_IPP);\n        }\n        as.setRight(in);\n\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  private static boolean isIPPorIMM2(Exprent first, Exprent second) {\n    if (first.type != Exprent.EXPRENT_ASSIGNMENT || second.type != Exprent.EXPRENT_ASSIGNMENT) {\n      return false;\n    }\n\n    AssignmentExprent af = (AssignmentExprent)first;\n    AssignmentExprent as = (AssignmentExprent)second;\n\n    if (as.getRight().type != Exprent.EXPRENT_FUNCTION) {\n      return false;\n    }\n\n    FunctionExprent func = (FunctionExprent)as.getRight();\n\n    if (func.getFuncType() != FunctionExprent.FUNCTION_ADD && func.getFuncType() != FunctionExprent.FUNCTION_SUB) {\n      return false;\n    }\n\n    Exprent econd = func.getLstOperands().get(0);\n    Exprent econst = func.getLstOperands().get(1);\n\n    if (econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && func.getFuncType() == FunctionExprent.FUNCTION_ADD) {\n      econd = econst;\n      econst = func.getLstOperands().get(0);\n    }\n\n    if (econst.type == Exprent.EXPRENT_CONST &&\n        ((ConstExprent)econst).hasValueOne() &&\n        af.getLeft().equals(econd) &&\n        af.getRight().equals(as.getLeft()) &&\n        (af.getLeft().getExprentUse() & Exprent.MULTIPLE_USES) != 0) {\n      int type = func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_IPP : FunctionExprent.FUNCTION_IMM;\n\n      FunctionExprent ret = new FunctionExprent(type, af.getRight(), func.bytecode);\n      ret.setImplicitType(VarType.VARTYPE_INT);\n\n      af.setRight(ret);\n      return true;\n    }\n\n    return false;\n  }\n  \n  private static boolean isMonitorExit(Exprent first) {\n    if (first.type == Exprent.EXPRENT_MONITOR) {\n      MonitorExprent expr = (MonitorExprent)first;\n      return expr.getMonType() == MonitorExprent.MONITOR_EXIT &&\n             expr.getValue().type == Exprent.EXPRENT_VAR &&\n             !((VarExprent)expr.getValue()).isStack();\n    }\n\n    return false;\n  }\n\n  private static boolean isQualifiedNewGetClass(Exprent first, Exprent second) {\n    if (first.type == Exprent.EXPRENT_INVOCATION) {\n      InvocationExprent invocation = (InvocationExprent)first;\n\n      if (!invocation.isStatic() && invocation.getInstance().type == Exprent.EXPRENT_VAR && invocation.getName().equals(\"getClass\") &&\n          invocation.getStringDescriptor().equals(\"()Ljava/lang/Class;\")) {\n\n        List<Exprent> lstExprents = second.getAllExprents();\n        lstExprents.add(second);\n\n        for (Exprent expr : lstExprents) {\n          if (expr.type == Exprent.EXPRENT_NEW) {\n            NewExprent newExpr = (NewExprent)expr;\n            if (newExpr.getConstructor() != null && !newExpr.getConstructor().getParameters().isEmpty() &&\n                newExpr.getConstructor().getParameters().get(0).equals(invocation.getInstance())) {\n\n              String classname = newExpr.getNewType().getValue();\n              ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);\n              if (node != null && node.type != ClassNode.CLASS_ROOT) {\n                return true;\n              }\n            }\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  // propagate (var = new X) forward to the <init> invocation\n  private static boolean isConstructorInvocationRemote(List<Exprent> list, int index) {\n    Exprent current = list.get(index);\n\n    if (current.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent as = (AssignmentExprent)current;\n\n      if (as.getLeft().type == Exprent.EXPRENT_VAR && as.getRight().type == Exprent.EXPRENT_NEW) {\n\n        NewExprent newExpr = (NewExprent)as.getRight();\n        VarType newType = newExpr.getNewType();\n        VarVersionPair leftPair = new VarVersionPair((VarExprent)as.getLeft());\n\n        if (newType.getType() == CodeConstants.TYPE_OBJECT && newType.getArrayDim() == 0 && newExpr.getConstructor() == null) {\n          for (int i = index + 1; i < list.size(); i++) {\n            Exprent remote = list.get(i);\n\n            // <init> invocation\n            if (remote.type == Exprent.EXPRENT_INVOCATION) {\n              InvocationExprent in = (InvocationExprent)remote;\n\n              if (in.getFuncType() == InvocationExprent.TYPE_INIT &&\n                  in.getInstance().type == Exprent.EXPRENT_VAR &&\n                  as.getLeft().equals(in.getInstance())) {\n                newExpr.setConstructor(in);\n                in.setInstance(null);\n\n                list.set(i, as.copy());\n\n                return true;\n              }\n            }\n\n            // check for variable in use\n            Set<VarVersionPair> setVars = remote.getAllVariables();\n            if (setVars.contains(leftPair)) { // variable used somewhere in between -> exit, need a better reduced code\n              return false;\n            }\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static Exprent isLambda(Exprent exprent, StructClass cl) {\n    List<Exprent> lst = exprent.getAllExprents();\n    for (Exprent expr : lst) {\n      Exprent ret = isLambda(expr, cl);\n      if (ret != null) {\n        exprent.replaceExprent(expr, ret);\n      }\n    }\n\n    if (exprent.type == Exprent.EXPRENT_INVOCATION) {\n      InvocationExprent in = (InvocationExprent)exprent;\n\n      if (in.getInvocationType() == InvocationExprent.INVOKE_DYNAMIC) {\n        String lambda_class_name = cl.qualifiedName + in.getInvokeDynamicClassSuffix();\n        ClassNode lambda_class = DecompilerContext.getClassProcessor().getMapRootClasses().get(lambda_class_name);\n\n        if (lambda_class != null) { // real lambda class found, replace invocation with an anonymous class\n          NewExprent newExpr = new NewExprent(new VarType(lambda_class_name, true), null, 0, in.bytecode);\n          newExpr.setConstructor(in);\n          // note: we don't set the instance to null with in.setInstance(null) like it is done for a common constructor invocation\n          // lambda can also be a reference to a virtual method (e.g. String x; ...(x::toString);)\n          // in this case instance will hold the corresponding object\n\n          return newExpr;\n        }\n      }\n    }\n\n    return null;\n  }\n\n  private static Exprent isSimpleConstructorInvocation(Exprent exprent) {\n    List<Exprent> lst = exprent.getAllExprents();\n    for (Exprent expr : lst) {\n      Exprent ret = isSimpleConstructorInvocation(expr);\n      if (ret != null) {\n        exprent.replaceExprent(expr, ret);\n      }\n    }\n\n    if (exprent.type == Exprent.EXPRENT_INVOCATION) {\n      InvocationExprent in = (InvocationExprent)exprent;\n      if (in.getFuncType() == InvocationExprent.TYPE_INIT && in.getInstance().type == Exprent.EXPRENT_NEW) {\n        NewExprent newExpr = (NewExprent)in.getInstance();\n        newExpr.setConstructor(in);\n        in.setInstance(null);\n        return newExpr;\n      }\n    }\n\n    return null;\n  }\n\n  private static boolean buildIff(Statement stat, SSAConstructorSparseEx ssa) {\n    if (stat.type == StatementType.IF && stat.getExprents() == null) {\n      IfStatement statement = (IfStatement)stat;\n      Exprent ifHeadExpr = statement.getHeadexprent();\n      Set<Integer> ifHeadExprBytecode = (ifHeadExpr == null ? null : ifHeadExpr.bytecode);\n\n      if (statement.iftype == IfStatement.IFTYPE_IFELSE) {\n        Statement ifStatement = statement.getIfstat();\n        Statement elseStatement = statement.getElsestat();\n\n        if (ifStatement.getExprents() != null && ifStatement.getExprents().size() == 1 &&\n            elseStatement.getExprents() != null && elseStatement.getExprents().size() == 1 &&\n            ifStatement.getAllSuccessorEdges().size() == 1 && elseStatement.getAllSuccessorEdges().size() == 1 &&\n            ifStatement.getAllSuccessorEdges().get(0).getDestination() == elseStatement.getAllSuccessorEdges().get(0).getDestination()) {\n          Exprent ifExpr = ifStatement.getExprents().get(0);\n          Exprent elseExpr = elseStatement.getExprents().get(0);\n\n          if (ifExpr.type == Exprent.EXPRENT_ASSIGNMENT && elseExpr.type == Exprent.EXPRENT_ASSIGNMENT) {\n            AssignmentExprent ifAssign = (AssignmentExprent)ifExpr;\n            AssignmentExprent elseAssign = (AssignmentExprent)elseExpr;\n\n            if (ifAssign.getLeft().type == Exprent.EXPRENT_VAR && elseAssign.getLeft().type == Exprent.EXPRENT_VAR) {\n              VarExprent ifVar = (VarExprent)ifAssign.getLeft();\n              VarExprent elseVar = (VarExprent)elseAssign.getLeft();\n\n              if (ifVar.getIndex() == elseVar.getIndex() && ifVar.isStack()) { // ifVar.getIndex() >= VarExprent.STACK_BASE) {\n                boolean found = false;\n\n                for (Entry<VarVersionPair, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) {\n                  if (ent.getKey().var == ifVar.getIndex()) {\n                    if (ent.getValue().contains(ifVar.getVersion()) && ent.getValue().contains(elseVar.getVersion())) {\n                      found = true;\n                      break;\n                    }\n                  }\n                }\n\n                if (found) {\n                  List<Exprent> data = new ArrayList<>(statement.getFirst().getExprents());\n\n                  List<Exprent> operands = Arrays.asList(statement.getHeadexprent().getCondition(), ifAssign.getRight(), elseAssign.getRight());\n                  data.add(new AssignmentExprent(ifVar, new FunctionExprent(FunctionExprent.FUNCTION_IIF, operands, ifHeadExprBytecode), ifHeadExprBytecode));\n                  statement.setExprents(data);\n\n                  if (statement.getAllSuccessorEdges().isEmpty()) {\n                    StatEdge ifEdge = ifStatement.getAllSuccessorEdges().get(0);\n                    StatEdge edge = new StatEdge(ifEdge.getType(), statement, ifEdge.getDestination());\n\n                    statement.addSuccessor(edge);\n                    if (ifEdge.closure != null) {\n                      ifEdge.closure.addLabeledEdge(edge);\n                    }\n                  }\n\n                  SequenceHelper.destroyAndFlattenStatement(statement);\n\n                  return true;\n                }\n              }\n            }\n          }\n          else if (ifExpr.type == Exprent.EXPRENT_EXIT && elseExpr.type == Exprent.EXPRENT_EXIT) {\n            ExitExprent ifExit = (ExitExprent)ifExpr;\n            ExitExprent elseExit = (ExitExprent)elseExpr;\n\n            if (ifExit.getExitType() == elseExit.getExitType() && ifExit.getValue() != null && elseExit.getValue() != null &&\n                ifExit.getExitType() == ExitExprent.EXIT_RETURN) {\n              // throw is dangerous, because of implicit casting to a common superclass\n              // e.g. throws IOException and throw true?new RuntimeException():new IOException(); won't work\n              if (ifExit.getExitType() == ExitExprent.EXIT_THROW &&\n                  !ifExit.getValue().getExprType().equals(elseExit.getValue().getExprType())) {  // note: getExprType unreliable at this point!\n                return false;\n              }\n\n              // avoid flattening to 'iff' if any of the branches is an 'iff' already\n              if (isIff(ifExit.getValue()) || isIff(elseExit.getValue())) {\n                return false;\n              }\n\n              List<Exprent> data = new ArrayList<>(statement.getFirst().getExprents());\n\n              data.add(new ExitExprent(ifExit.getExitType(), new FunctionExprent(FunctionExprent.FUNCTION_IIF,\n                                                                               Arrays.asList(\n                                                                                 statement.getHeadexprent().getCondition(),\n                                                                                 ifExit.getValue(),\n                                                                                 elseExit.getValue()), ifHeadExprBytecode), ifExit.getRetType(), ifHeadExprBytecode));\n              statement.setExprents(data);\n\n              StatEdge retEdge = ifStatement.getAllSuccessorEdges().get(0);\n              Statement closure = retEdge.closure == statement ? statement.getParent() : retEdge.closure;\n              statement.addSuccessor(new StatEdge(EdgeType.BREAK, statement, retEdge.getDestination(), closure));\n\n              SequenceHelper.destroyAndFlattenStatement(statement);\n\n              return true;\n            }\n          }\n        }\n      }\n    }\n\n    return false;\n  }\n\n  private static boolean isIff(Exprent exp) {\n    return exp.type == Exprent.EXPRENT_FUNCTION && ((FunctionExprent) exp).getFuncType() == FunctionExprent.FUNCTION_IIF;\n  }\n\n  private static boolean collapseInlinedClass14(Statement stat) {\n    boolean ret = class14Builder.match(stat);\n    if (ret) {\n      String class_name = (String)class14Builder.getVariableValue(\"$classname$\");\n      AssignmentExprent assignment = (AssignmentExprent)class14Builder.getVariableValue(\"$assignfield$\");\n      FieldExprent fieldExpr = (FieldExprent)class14Builder.getVariableValue(\"$field$\");\n\n      assignment.replaceExprent(assignment.getRight(), new ConstExprent(VarType.VARTYPE_CLASS, class_name, null));\n\n      List<Exprent> data = new ArrayList<>(stat.getFirst().getExprents());\n\n      stat.setExprents(data);\n\n      SequenceHelper.destroyAndFlattenStatement(stat);\n\n      ClassWrapper wrapper = (ClassWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_WRAPPER);\n      if (wrapper != null) {\n        wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fieldExpr.getName(), fieldExpr.getDescriptor().descriptorString));\n      }\n    }\n\n    return ret;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode.DirectNodeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;\nimport org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;\nimport org.jetbrains.java.decompiler.util.SFormsFastMapDirect;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class StackVarsProcessor {\n  public static void simplifyStackVars(RootStatement root, StructMethod mt, StructClass cl) {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n\n    Set<Integer> setReorderedIfs = new HashSet<>();\n    SSAUConstructorSparseEx ssau = null;\n\n    while (true) {\n      cancellationManager.checkCanceled();\n      boolean found = false;\n\n      SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();\n      ssa.splitVariables(root, mt);\n\n      SimplifyExprentsHelper sehelper = new SimplifyExprentsHelper(ssau == null);\n      while (sehelper.simplifyStackVarsStatement(root, setReorderedIfs, ssa, cl)) {\n        cancellationManager.checkCanceled();\n        found = true;\n      }\n\n      setVersionsToNull(root);\n\n      SequenceHelper.condenseSequences(root);\n\n      ssau = new SSAUConstructorSparseEx();\n      ssau.splitVariables(root, mt);\n      cancellationManager.checkCanceled();\n      if (iterateStatements(root, ssau)) {\n        found = true;\n      }\n\n      setVersionsToNull(root);\n\n      if (!found) {\n        break;\n      }\n    }\n\n    // remove unused assignments\n    ssau = new SSAUConstructorSparseEx();\n    ssau.splitVariables(root, mt);\n\n    iterateStatements(root, ssau);\n\n    setVersionsToNull(root);\n  }\n\n  private static void setVersionsToNull(Statement stat) {\n    if (stat.getExprents() == null) {\n      for (Object obj : stat.getSequentialObjects()) {\n        if (obj instanceof Statement) {\n          setVersionsToNull((Statement)obj);\n        }\n        else if (obj instanceof Exprent) {\n          setExprentVersionsToNull((Exprent)obj);\n        }\n      }\n    }\n    else {\n      for (Exprent exprent : stat.getExprents()) {\n        setExprentVersionsToNull(exprent);\n      }\n    }\n  }\n\n  private static void setExprentVersionsToNull(Exprent exprent) {\n    List<Exprent> lst = exprent.getAllExprents(true);\n    lst.add(exprent);\n\n    for (Exprent expr : lst) {\n      if (expr.type == Exprent.EXPRENT_VAR) {\n        ((VarExprent)expr).setVersion(0);\n      }\n    }\n  }\n\n  private static boolean iterateStatements(RootStatement root, SSAUConstructorSparseEx ssa) {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n\n    FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();\n    DirectGraph dgraph = flatthelper.buildDirectGraph(root);\n\n    boolean res = false;\n\n    Set<DirectNode> setVisited = new HashSet<>();\n    LinkedList<DirectNode> stack = new LinkedList<>();\n    LinkedList<Map<VarVersionPair, Exprent>> stackMaps = new LinkedList<>();\n\n    stack.add(dgraph.first);\n    stackMaps.add(new HashMap<>());\n\n    while (!stack.isEmpty()) {\n      cancellationManager.checkCanceled();\n      DirectNode nd = stack.removeFirst();\n      Map<VarVersionPair, Exprent> mapVarValues = stackMaps.removeFirst();\n\n      if (setVisited.contains(nd)) {\n        continue;\n      }\n      setVisited.add(nd);\n\n      List<List<Exprent>> lstLists = new ArrayList<>();\n\n      if (!nd.exprents.isEmpty()) {\n        lstLists.add(nd.exprents);\n      }\n\n      if (nd.successors.size() == 1) {\n        DirectNode ndsucc = nd.successors.get(0);\n        if (ndsucc.type == DirectNodeType.TAIL && !ndsucc.exprents.isEmpty()) {\n          lstLists.add(nd.successors.get(0).exprents);\n          nd = ndsucc;\n        }\n      }\n\n      for (int i = 0; i < lstLists.size(); i++) {\n        List<Exprent> lst = lstLists.get(i);\n\n        int index = 0;\n        while (index < lst.size()) {\n          Exprent next = null;\n          if (index == lst.size() - 1) {\n            if (i < lstLists.size() - 1) {\n              next = lstLists.get(i + 1).get(0);\n            }\n          }\n          else {\n            next = lst.get(index + 1);\n          }\n\n          int[] ret = iterateExprent(lst, index, next, mapVarValues, ssa);\n          if (ret[0] >= 0) {\n            index = ret[0];\n          }\n          else {\n            index++;\n          }\n          res |= (ret[1] == 1);\n        }\n      }\n\n      for (DirectNode ndx : nd.successors) {\n        stack.add(ndx);\n        stackMaps.add(new HashMap<>(mapVarValues));\n      }\n\n      // make sure the 3 special exprent lists in a loop (init, condition, increment) are not empty\n      // change loop type if necessary\n      if (nd.exprents.isEmpty() &&\n          (nd.type == DirectNodeType.INIT || nd.type == DirectNodeType.CONDITION || nd.type == DirectNodeType.INCREMENT)) {\n        nd.exprents.add(null);\n\n        if (nd.statement.type == StatementType.DO) {\n          DoStatement loop = (DoStatement)nd.statement;\n\n          if (loop.getLoopType() == LoopType.FOR &&\n              loop.getInitExprent() == null &&\n              loop.getIncExprent() == null) { // \"downgrade\" loop to 'while'\n            loop.setLoopType(LoopType.WHILE);\n          }\n        }\n      }\n    }\n\n    return res;\n  }\n\n  private static Exprent isReplaceableVar(Exprent exprent, Map<VarVersionPair, Exprent> mapVarValues) {\n    Exprent dest = null;\n    if (exprent.type == Exprent.EXPRENT_VAR) {\n      VarExprent var = (VarExprent)exprent;\n      dest = mapVarValues.get(new VarVersionPair(var));\n    }\n    return dest;\n  }\n\n  private static void replaceSingleVar(Exprent parent, VarExprent var, Exprent dest, SSAUConstructorSparseEx ssau) {\n    parent.replaceExprent(var, dest);\n\n    // live sets\n    SFormsFastMapDirect livemap = ssau.getLiveVarVersionsMap(new VarVersionPair(var));\n    Set<VarVersionPair> setVars = getAllVersions(dest);\n\n    for (VarVersionPair varpaar : setVars) {\n      VarVersionNode node = ssau.getSsuversions().nodes.getWithKey(varpaar);\n\n      for (Iterator<Entry<Integer, FastSparseSet<Integer>>> itent = node.live.entryList().iterator(); itent.hasNext(); ) {\n        Entry<Integer, FastSparseSet<Integer>> ent = itent.next();\n\n        Integer key = ent.getKey();\n\n        if (!livemap.containsKey(key)) {\n          itent.remove();\n        }\n        else {\n          FastSparseSet<Integer> set = ent.getValue();\n\n          set.complement(livemap.get(key));\n          if (set.isEmpty()) {\n            itent.remove();\n          }\n        }\n      }\n    }\n  }\n\n  private static int[] iterateExprent(List<Exprent> lstExprents,\n                                      int index,\n                                      Exprent next,\n                                      Map<VarVersionPair, Exprent> mapVarValues,\n                                      SSAUConstructorSparseEx ssau) {\n    Exprent exprent = lstExprents.get(index);\n\n    int changed = 0;\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n\n    for (Exprent expr : exprent.getAllExprents()) {\n      while (true) {\n        cancellationManager.checkCanceled();\n        Object[] arr = iterateChildExprent(expr, exprent, next, mapVarValues, ssau);\n        Exprent retexpr = (Exprent)arr[0];\n        changed |= (Boolean)arr[1] ? 1 : 0;\n\n        boolean isReplaceable = (Boolean)arr[2];\n        if (retexpr != null) {\n          if (isReplaceable) {\n            replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau);\n            expr = retexpr;\n          }\n          else {\n            exprent.replaceExprent(expr, retexpr);\n          }\n          changed = 1;\n        }\n\n        if (!isReplaceable) {\n          break;\n        }\n      }\n    }\n\n    // no var on the highest level, so no replacing\n\n    VarExprent left = null;\n    Exprent right = null;\n\n    if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent as = (AssignmentExprent)exprent;\n      if (as.getLeft().type == Exprent.EXPRENT_VAR) {\n        left = (VarExprent)as.getLeft();\n        right = as.getRight();\n      }\n    }\n\n    if (left == null) {\n      return new int[]{-1, changed};\n    }\n\n    VarVersionPair leftpaar = new VarVersionPair(left);\n\n    List<VarVersionNode> usedVers = new ArrayList<>();\n    boolean notdom = getUsedVersions(ssau, leftpaar, usedVers);\n\n    if (!notdom && usedVers.isEmpty()) {\n      if (left.isStack() && (right.type == Exprent.EXPRENT_INVOCATION ||\n                             right.type == Exprent.EXPRENT_ASSIGNMENT || right.type == Exprent.EXPRENT_NEW)) {\n        if (right.type == Exprent.EXPRENT_NEW) {\n          // new Object(); permitted\n          NewExprent nexpr = (NewExprent)right;\n          if (nexpr.isAnonymous() || nexpr.getNewType().getArrayDim() > 0\n              || nexpr.getNewType().getType() != CodeConstants.TYPE_OBJECT) {\n            return new int[]{-1, changed};\n          }\n        }\n\n        lstExprents.set(index, right);\n        return new int[]{index + 1, 1};\n      }\n      else if (right.type == Exprent.EXPRENT_VAR) {\n        lstExprents.remove(index);\n        return new int[]{index, 1};\n      }\n      else {\n        return new int[]{-1, changed};\n      }\n    }\n\n    int useflags = right.getExprentUse();\n\n    // stack variables only\n    if (!left.isStack() &&\n        (right.type != Exprent.EXPRENT_VAR || ((VarExprent)right).isStack())) { // special case catch(... ex)\n      return new int[]{-1, changed};\n    }\n\n    if ((useflags & Exprent.MULTIPLE_USES) == 0 && (notdom || usedVers.size() > 1)) {\n      return new int[]{-1, changed};\n    }\n\n    Map<Integer, Set<VarVersionPair>> mapVars = getAllVarVersions(leftpaar, right, ssau);\n\n    boolean isSelfReference = mapVars.containsKey(leftpaar.var);\n    if (isSelfReference && notdom) {\n      return new int[]{-1, changed};\n    }\n\n    Set<VarVersionPair> setNextVars = next == null ? null : getAllVersions(next);\n\n    // FIXME: fix the entire method!\n    if (right.type != Exprent.EXPRENT_CONST &&\n        right.type != Exprent.EXPRENT_VAR &&\n        setNextVars != null &&\n        mapVars.containsKey(leftpaar.var)) {\n      for (VarVersionNode usedvar : usedVers) {\n        if (!setNextVars.contains(new VarVersionPair(usedvar.var, usedvar.version))) {\n          return new int[]{-1, changed};\n        }\n      }\n    }\n\n    mapVars.remove(leftpaar.var);\n\n    boolean vernotreplaced = false;\n    boolean verreplaced = false;\n\n    Set<VarVersionPair> setTempUsedVers = new HashSet<>();\n\n    for (VarVersionNode usedvar : usedVers) {\n      VarVersionPair usedver = new VarVersionPair(usedvar.var, usedvar.version);\n      if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) &&\n          (right.type == Exprent.EXPRENT_CONST || right.type == Exprent.EXPRENT_VAR || right.type == Exprent.EXPRENT_FIELD\n           || setNextVars == null || setNextVars.contains(usedver))) {\n\n        setTempUsedVers.add(usedver);\n        verreplaced = true;\n      }\n      else {\n        vernotreplaced = true;\n      }\n    }\n\n    //workaround to preserve variable names\n    AssignmentExprent assignmentExprent = (AssignmentExprent)exprent;\n    if (assignmentExprent.getRight() instanceof VarExprent && assignmentExprent.getLeft() instanceof VarExprent leftExp) {\n      StructMethod method = leftExp.getProcessor().getMethod();\n      StructLocalVariableTableAttribute attr =\n        method.getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);\n      if (attr != null) {\n        String signature = attr.getDescriptor(leftExp.getIndex(), leftExp.getVisibleOffset());\n        if (signature != null) {\n          return new int[]{-1, changed};\n        }\n      }\n    }\n\n    if (isSelfReference && vernotreplaced) {\n      return new int[]{-1, changed};\n    }\n    else {\n      for (VarVersionPair usedver : setTempUsedVers) {\n        Exprent copy = right.copy();\n        if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) {\n          ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id));\n        }\n\n        mapVarValues.put(usedver, copy);\n      }\n    }\n\n    if (!notdom && !vernotreplaced) {\n      // remove assignment\n      lstExprents.remove(index);\n      return new int[]{index, 1};\n    }\n    else if (verreplaced) {\n      return new int[]{index + 1, changed};\n    }\n    else {\n      return new int[]{-1, changed};\n    }\n  }\n\n  private static Set<VarVersionPair> getAllVersions(Exprent exprent) {\n    Set<VarVersionPair> res = new HashSet<>();\n\n    List<Exprent> listTemp = new ArrayList<>(exprent.getAllExprents(true));\n    listTemp.add(exprent);\n\n    for (Exprent expr : listTemp) {\n      if (expr.type == Exprent.EXPRENT_VAR) {\n        VarExprent var = (VarExprent)expr;\n        res.add(new VarVersionPair(var));\n      }\n    }\n\n    return res;\n  }\n\n  private static Object[] iterateChildExprent(Exprent exprent,\n                                              Exprent parent,\n                                              Exprent next,\n                                              Map<VarVersionPair, Exprent> mapVarValues,\n                                              SSAUConstructorSparseEx ssau) {\n    boolean changed = false;\n\n    for (Exprent expr : exprent.getAllExprents()) {\n      while (true) {\n        Object[] arr = iterateChildExprent(expr, parent, next, mapVarValues, ssau);\n        Exprent retexpr = (Exprent)arr[0];\n        changed |= (Boolean)arr[1];\n\n        boolean isReplaceable = (Boolean)arr[2];\n        if (retexpr != null) {\n          if (isReplaceable) {\n            replaceSingleVar(exprent, (VarExprent)expr, retexpr, ssau);\n            expr = retexpr;\n          }\n          else {\n            exprent.replaceExprent(expr, retexpr);\n          }\n          changed = true;\n        }\n\n        if (!isReplaceable) {\n          break;\n        }\n      }\n    }\n\n    Exprent dest = isReplaceableVar(exprent, mapVarValues);\n    if (dest != null) {\n      return new Object[]{dest, true, true};\n    }\n\n\n    VarExprent left = null;\n    Exprent right = null;\n\n    if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n      AssignmentExprent as = (AssignmentExprent)exprent;\n      if (as.getLeft().type == Exprent.EXPRENT_VAR) {\n        left = (VarExprent)as.getLeft();\n        right = as.getRight();\n      }\n    }\n\n    if (left == null) {\n      return new Object[]{null, changed, false};\n    }\n\n    boolean isHeadSynchronized = false;\n    if (next == null && parent.type == Exprent.EXPRENT_MONITOR) {\n      MonitorExprent monexpr = (MonitorExprent)parent;\n      if (monexpr.getMonType() == MonitorExprent.MONITOR_ENTER && exprent.equals(monexpr.getValue())) {\n        isHeadSynchronized = true;\n      }\n    }\n\n    // stack variable or synchronized head exprent\n    if (!left.isStack() && !isHeadSynchronized) {\n      return new Object[]{null, changed, false};\n    }\n\n    VarVersionPair leftpaar = new VarVersionPair(left);\n\n    List<VarVersionNode> usedVers = new ArrayList<>();\n    boolean notdom = getUsedVersions(ssau, leftpaar, usedVers);\n\n    if (!notdom && usedVers.isEmpty()) {\n      return new Object[]{right, changed, false};\n    }\n\n    // stack variables only\n    if (!left.isStack()) {\n      return new Object[]{null, changed, false};\n    }\n\n    int useflags = right.getExprentUse();\n\n    if ((useflags & Exprent.BOTH_FLAGS) != Exprent.BOTH_FLAGS) {\n      return new Object[]{null, changed, false};\n    }\n\n    Map<Integer, Set<VarVersionPair>> mapVars = getAllVarVersions(leftpaar, right, ssau);\n    if (mapVars.containsKey(leftpaar.var) && notdom) {\n      return new Object[]{null, changed, false};\n    }\n\n    mapVars.remove(leftpaar.var);\n\n    Set<VarVersionPair> setAllowedVars = getAllVersions(parent);\n    if (next != null) {\n      setAllowedVars.addAll(getAllVersions(next));\n    }\n\n    boolean vernotreplaced = false;\n\n    Set<VarVersionPair> setTempUsedVers = new HashSet<>();\n\n    for (VarVersionNode usedvar : usedVers) {\n      VarVersionPair usedver = new VarVersionPair(usedvar.var, usedvar.version);\n      if (isVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) &&\n          (right.type == Exprent.EXPRENT_VAR || setAllowedVars.contains(usedver))) {\n\n        setTempUsedVers.add(usedver);\n      }\n      else {\n        vernotreplaced = true;\n      }\n    }\n\n    if (!notdom && !vernotreplaced) {\n      for (VarVersionPair usedver : setTempUsedVers) {\n        Exprent copy = right.copy();\n        if (right.type == Exprent.EXPRENT_FIELD && ssau.getMapFieldVars().containsKey(right.id)) {\n          ssau.getMapFieldVars().put(copy.id, ssau.getMapFieldVars().get(right.id));\n        }\n\n        mapVarValues.put(usedver, copy);\n      }\n\n      // remove assignment\n      return new Object[]{right, changed, false};\n    }\n\n    return new Object[]{null, changed, false};\n  }\n\n  private static boolean getUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPair var, List<? super VarVersionNode> res) {\n    VarVersionsGraph ssuversions = ssa.getSsuversions();\n    VarVersionNode varnode = ssuversions.nodes.getWithKey(var);\n\n    Set<VarVersionNode> setVisited = new HashSet<>();\n    Set<VarVersionNode> setNotDoms = new HashSet<>();\n\n    LinkedList<VarVersionNode> stack = new LinkedList<>();\n    stack.add(varnode);\n\n    while (!stack.isEmpty()) {\n      VarVersionNode nd = stack.remove(0);\n      setVisited.add(nd);\n\n      if (nd != varnode && (nd.flags & VarVersionNode.FLAG_PHANTOM_FIN_EXIT) == 0) {\n        res.add(nd);\n      }\n\n      for (VarVersionEdge edge : nd.successors) {\n        VarVersionNode succ = edge.dest;\n\n        if (!setVisited.contains(edge.dest)) {\n\n          boolean isDominated = true;\n          for (VarVersionEdge prededge : succ.predecessors) {\n            if (!setVisited.contains(prededge.source)) {\n              isDominated = false;\n              break;\n            }\n          }\n\n          if (isDominated) {\n            stack.add(succ);\n          }\n          else {\n            setNotDoms.add(succ);\n          }\n        }\n      }\n    }\n\n    setNotDoms.removeAll(setVisited);\n\n    return !setNotDoms.isEmpty();\n  }\n\n  private static boolean isVersionToBeReplaced(VarVersionPair usedvar,\n                                               Map<Integer, Set<VarVersionPair>> mapVars,\n                                               SSAUConstructorSparseEx ssau,\n                                               VarVersionPair leftpaar) {\n    VarVersionsGraph ssuversions = ssau.getSsuversions();\n\n    SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(usedvar);\n    if (mapLiveVars == null) {\n      // dummy version, predecessor of a phi node\n      return false;\n    }\n\n    // compare protected ranges\n    if (!Objects.equals(ssau.getMapVersionFirstRange().get(leftpaar), ssau.getMapVersionFirstRange().get(usedvar))) {\n      return false;\n    }\n\n    for (Entry<Integer, Set<VarVersionPair>> ent : mapVars.entrySet()) {\n      FastSparseSet<Integer> liveverset = mapLiveVars.get(ent.getKey());\n      if (liveverset == null) {\n        return false;\n      }\n\n      Set<VarVersionNode> domset = new HashSet<>();\n      for (VarVersionPair verpaar : ent.getValue()) {\n        domset.add(ssuversions.nodes.getWithKey(verpaar));\n      }\n\n      boolean isdom = false;\n\n      for (Integer livever : liveverset) {\n        VarVersionNode node = ssuversions.nodes.getWithKey(new VarVersionPair(ent.getKey().intValue(), livever.intValue()));\n\n        if (ssuversions.isDominatorSet(node, domset)) {\n          isdom = true;\n          break;\n        }\n      }\n\n      if (!isdom) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  private static Map<Integer, Set<VarVersionPair>> getAllVarVersions(VarVersionPair leftvar,\n                                                                     Exprent exprent,\n                                                                     SSAUConstructorSparseEx ssau) {\n    Map<Integer, Set<VarVersionPair>> map = new HashMap<>();\n    SFormsFastMapDirect mapLiveVars = ssau.getLiveVarVersionsMap(leftvar);\n\n    List<Exprent> lst = exprent.getAllExprents(true);\n    lst.add(exprent);\n\n    for (Exprent expr : lst) {\n      if (expr.type == Exprent.EXPRENT_VAR) {\n        int varindex = ((VarExprent)expr).getIndex();\n        if (leftvar.var != varindex) {\n          if (mapLiveVars.containsKey(varindex)) {\n            Set<VarVersionPair> verset = new HashSet<>();\n            for (Integer vers : mapLiveVars.get(varindex)) {\n              verset.add(new VarVersionPair(varindex, vers.intValue()));\n            }\n            map.put(varindex, verset);\n          }\n          else {\n            throw new RuntimeException(\"inkonsistent live map!\");\n          }\n        }\n        else {\n          map.put(varindex, null);\n        }\n      }\n      else if (expr.type == Exprent.EXPRENT_FIELD) {\n        if (ssau.getMapFieldVars().containsKey(expr.id)) {\n          int varindex = ssau.getMapFieldVars().get(expr.id);\n          if (mapLiveVars.containsKey(varindex)) {\n            Set<VarVersionPair> verset = new HashSet<>();\n            for (Integer vers : mapLiveVars.get(varindex)) {\n              verset.add(new VarVersionPair(varindex, vers.intValue()));\n            }\n            map.put(varindex, verset);\n          }\n        }\n      }\n    }\n\n    return map;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class StatEdge {\n  private @NotNull EdgeType type;\n  private Statement source;\n  private Statement destination;\n  private List<String> exceptions;\n  public Statement closure;\n  public boolean labeled = true;\n  public boolean explicit = true;\n\n  private StatEdge(@NotNull EdgeType type,\n                  Statement source,\n                  Statement destination,\n                  List<String> exceptions,\n                  Statement closure,\n                  boolean labeled,\n                  boolean explicit) {\n    this.type = type;\n    this.source = source;\n    this.destination = destination;\n    this.exceptions = exceptions;\n    this.closure = closure;\n    this.labeled = labeled;\n    this.explicit = explicit;\n  }\n\n  public StatEdge(@NotNull EdgeType type, Statement source, Statement destination, Statement closure) {\n    this(type, source, destination);\n    this.closure = closure;\n  }\n\n  public StatEdge(@NotNull EdgeType type, Statement source, Statement destination) {\n    this.type = type;\n    this.source = source;\n    this.destination = destination;\n  }\n\n  public StatEdge(Statement source, Statement destination, List<String> exceptions) {\n    this(EdgeType.EXCEPTION, source, destination);\n    if (exceptions != null) {\n      this.exceptions = new ArrayList<>(exceptions);\n    }\n  }\n\n  public @NotNull EdgeType getType() {\n    return type;\n  }\n  public void setType(@NotNull EdgeType type) {\n    this.type = type;\n  }\n\n  public Statement getSource() {\n    return source;\n  }\n  public void setSource(Statement source) {\n    this.source = source;\n  }\n\n  public Statement getDestination() {\n    return destination;\n  }\n  public void setDestination(Statement destination) {\n    this.destination = destination;\n  }\n\n  public List<String> getExceptions() {\n    return this.exceptions;\n  }\n\n  public StatEdge copy() {\n    return new StatEdge(type, source, destination, exceptions, closure, labeled, explicit);\n  }\n  /**\n   * Type of the edges between statements.\n   * @see Statement\n   */\n  public interface EdgeType {\n    EdgeType REGULAR = new EdgeType() {\n      @Override\n      public int mask() {\n        return 1;\n      }\n\n      @Override\n      public String toString() {\n        return \"REGULAR\";\n      }\n    };\n\n    EdgeType EXCEPTION = new EdgeType() {\n      @Override\n      public int mask() {\n        return 2;\n      }\n\n      @Override\n      public String toString() {\n        return \"EXCEPTION\";\n      }\n    };\n\n    EdgeType BREAK = new EdgeType() {\n      @Override\n      public int mask() {\n        return 4;\n      }\n\n      @Override\n      public String toString() {\n        return \"BREAK\";\n      }\n    };\n\n    EdgeType CONTINUE = new EdgeType() {\n      @Override\n      public int mask() {\n        return 8;\n      }\n\n      @Override\n      public String toString() {\n        return \"CONTINUE\";\n      }\n    };\n\n    EdgeType FINALLY_EXIT = new EdgeType() {\n      @Override\n      public int mask() {\n        return 32;\n      }\n\n      @Override\n      public String toString() {\n        return \"FINALLY_EXIT\";\n      }\n    };\n\n    EdgeType ALL = new EdgeType() {\n      @Override\n      public int mask() {\n        return 0x80000000;\n      }\n\n      @Override\n      public @NotNull EdgeType unite(@NotNull EdgeType other) {\n        return this;\n      }\n\n      @Override\n      public String toString() {\n        return \"ALL\";\n      }\n    };\n\n    EdgeType DIRECT_ALL = new EdgeType() {\n      @Override\n      public int mask() {\n        return 0x40000000;\n      }\n\n      @Override\n      public @NotNull EdgeType unite(@NotNull EdgeType other) {\n        return this;\n      }\n\n      @Override\n      public String toString() {\n        return \"DIRECT_ALL\";\n      }\n    };\n\n    EdgeType NULL = new EdgeType() {\n      @Override\n      public int mask() {\n        return -1;\n      }\n\n      @Override\n      public @NotNull EdgeType unite(@NotNull EdgeType other) {\n        throw new UnsupportedOperationException(\"Union operation is not supported for NULL edge type\");\n      }\n    };\n\n    int mask();\n\n    default @NotNull EdgeType unite(@NotNull EdgeType other) {\n      return new EdgeType() {\n        @Override\n        public int mask() {\n          return EdgeType.this.mask() | other.mask();\n        }\n      };\n    }\n\n    static EdgeType[] types() {\n      return new EdgeType[] {REGULAR, EXCEPTION, BREAK, CONTINUE, FINALLY_EXIT};\n    }\n  }\n\n  /**\n   * Represents a direction of edge.\n   * Backward for input edges, forward for output edges.\n   */\n  public enum EdgeDirection {\n    BACKWARD,\n    FORWARD\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.util.ListStack;\n\nimport java.util.*;\n\n/**\n * The class finds the strongly connected components (SCCs) of a directed graph,\n * implementing \"Tarjan's strongly connected components\" algorithm.\n * Running time is linear.\n */\n// todo should be replaced or reuse InferenceGraphNode.strongConnect or DFSTBuilder.Tarjan?\npublic class StrongConnectivityHelper {\n  private final List<List<Statement>> components = new ArrayList<>();\n  private final Set<Statement> setProcessed = new HashSet<>();\n  private final ListStack<Statement> component = new ListStack<>();\n  private final Set<Statement> visited = new HashSet<>();\n  private final Map<Statement, Integer> indices = new HashMap<>();\n  private final Map<Statement, Integer> lowIndices = new HashMap<>();\n\n  private int nextIndex;\n\n  public StrongConnectivityHelper(@NotNull Statement startStatement) {\n    visitTree(startStatement.getFirst());\n    for (Statement statement : startStatement.getStats()) {\n      if (!setProcessed.contains(statement) && statement.getPredecessorEdges(EdgeType.DIRECT_ALL).isEmpty()) {\n        visitTree(statement);\n      }\n    }\n    // should not find any more nodes! FIXME: ??\n    for (Statement statement : startStatement.getStats()) {\n      if (!setProcessed.contains(statement)) {\n        visitTree(statement);\n      }\n    }\n  }\n\n  private void visitTree(@NotNull Statement statement) {\n    component.clear();\n    visited.clear();\n    indices.clear();\n    lowIndices.clear();\n    nextIndex = 0;\n\n    visit(statement);\n\n    setProcessed.addAll(visited);\n    setProcessed.add(statement);\n  }\n\n  private void visit(@NotNull Statement statement) {\n    component.push(statement);\n    indices.put(statement, nextIndex);\n    lowIndices.put(statement, nextIndex);\n    nextIndex++;\n    List<Statement> successors = statement.getNeighbours(EdgeType.REGULAR, EdgeDirection.FORWARD); // TODO: set?\n    successors.removeAll(setProcessed);\n    for (Statement successor : successors) {\n      int successorIndex;\n      if (visited.contains(successor)) {\n        successorIndex = indices.get(successor);\n      }\n      else {\n        visited.add(successor);\n        visit(successor);\n        successorIndex = lowIndices.get(successor);\n      }\n      lowIndices.put(statement, Math.min(lowIndices.get(statement), successorIndex));\n    }\n    if (lowIndices.get(statement).intValue() == indices.get(statement).intValue()) {\n      List<Statement> component = new ArrayList<>();\n      Statement statementInComponent;\n      do {\n        statementInComponent = this.component.pop();\n        component.add(statementInComponent);\n      }\n      while (statementInComponent != statement);\n      components.add(component);\n    }\n  }\n\n  public static boolean isExitComponent(@NotNull List<? extends Statement> component) {\n    Set<Statement> statements = new HashSet<>();\n    for (Statement statement : component) {\n      statements.addAll(statement.getNeighbours(EdgeType.REGULAR, EdgeDirection.FORWARD));\n    }\n    for (Statement statement : component) {\n      statements.remove(statement);\n    }\n    return statements.size() == 0;\n  }\n\n  public @NotNull List<Statement> getExitReps() {\n    List<Statement> result = new ArrayList<>();\n    for (List<Statement> component : components) {\n      if (isExitComponent(component)) {\n        result.add(component.get(0));\n      }\n    }\n    return result;\n  }\n\n  public @NotNull List<@NotNull List<Statement>> getComponents() {\n    return components;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/SwitchHelper.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.ClassNameConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.struct.StructClass;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static org.jetbrains.java.decompiler.code.CodeConstants.CLINIT_NAME;\nimport static org.jetbrains.java.decompiler.main.extern.IFernflowerLogger.Severity;\nimport static org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent.FUNCTION_CAST;\nimport static org.jetbrains.java.decompiler.struct.gen.VarType.VARTYPE_STRING;\n\npublic final class SwitchHelper {\n\n  /**\n   * Method simplifies <code>switchStatement</code> if it represents switch-on-enum pattern.\n   *\n   * @param switchStatement statement to transform\n   */\n  public static void simplifySwitchOnEnum(@NotNull SwitchStatement switchStatement) {\n    SwitchExprent switchExprent = (SwitchExprent)switchStatement.getHeadExprent();\n    Exprent value = Objects.requireNonNull(switchExprent).getValue();\n    if (!isEnumArray(value)) return;\n    List<List<@Nullable Exprent>> caseValues = switchStatement.getCaseValues();\n    ArrayExprent array = (ArrayExprent)value;\n    Map<Exprent, Exprent> mapping = evaluateCaseLabelsToFieldsMapping(caseValues, array);\n    List<List<@Nullable Exprent>> realCaseValues = findRealCaseValues(caseValues, mapping);\n    if (realCaseValues == null) return;\n    caseValues.clear();\n    caseValues.addAll(realCaseValues);\n    switchExprent.replaceExprent(value, ((InvocationExprent)array.getIndex()).getInstance().copy());\n  }\n\n  /**\n   * Method searches and simplifies \"switch-on-references\" patterns in the statement graph.\n   *\n   * @param root statement to start traversal\n   * @param cl - struct class to check java version\n   */\n  public static void simplifySwitchesOnReferences(@NotNull RootStatement root, StructClass cl) {\n    List<SwitchOnCandidate> candidates = new ArrayList<>();\n    ArrayList<SwitchRecognizer> recognizers = new ArrayList<>(\n      Arrays.asList(new StringSwitchRecognizer.JavacStringRecognizer(), new StringSwitchRecognizer.EcjStringRecognizer()));\n    if (cl.hasRecordPatternSupport() && DecompilerContext.getOption(IFernflowerPreferences.CONVERT_PATTERN_SWITCH)) {\n      recognizers.add(new SwitchPatternHelper.JavacReferenceRecognizer());\n    }\n    collectSwitchesOn(root, recognizers, candidates, new HashSet<>());\n    if (candidates.isEmpty()) return;\n    List<TempVarAssignmentItem> tempVarAssignments = new ArrayList<>();\n    candidates.forEach(candidate -> tempVarAssignments.addAll(candidate.prepareTempAssignments()));\n    if (!checkAssignmentsToDelete(root, tempVarAssignments)) {\n      return;\n    }\n    candidates.forEach(candidate -> candidate.simplify());\n    removeTempVariableDeclarations(tempVarAssignments);\n  }\n\n  /**\n   * @return true, if all similar nested assignments have been processed, false otherwise,\n   * it helps to prevent some false positive cases\n   */\n  static boolean checkAssignmentsToDelete(@NotNull Statement parent,\n                                          @NotNull List<TempVarAssignmentItem> items) {\n    Map<VarExprent, List<VarExprent>> collected = items.stream().map(t -> t.varExprent()).collect(Collectors.groupingBy(t -> t));\n    return checkRecursivelyAssignmentsToDelete(parent, collected);\n  }\n\n  private static boolean checkRecursivelyAssignmentsToDelete(@NotNull Statement statement,\n                                                             @NotNull Map<VarExprent, List<VarExprent>> collected) {\n    List<Exprent> exprents = statement.getExprents();\n    if (exprents != null) {\n      for (Exprent exprent : exprents) {\n        if (!(exprent instanceof AssignmentExprent assignmentExprent)) {\n          continue;\n        }\n        Exprent right = assignmentExprent.getRight();\n        Exprent left = assignmentExprent.getLeft();\n        boolean containsLeft = containVar(collected, left);\n        if (right instanceof VarExprent varExprent) {\n          boolean containsRight = containVar(collected, varExprent);\n          if (containsLeft || !containsRight) {\n            continue;\n          }\n          return false;\n        }\n        if (right instanceof FunctionExprent functionExprent &&\n            functionExprent.getFuncType() == FUNCTION_CAST &&\n            functionExprent.getLstOperands().size() == 2 &&\n            functionExprent.getLstOperands().get(1) instanceof ConstExprent) {\n          Exprent operand = functionExprent.getLstOperands().get(0);\n          if (operand instanceof VarExprent varExprent) {\n            boolean containsRight = containVar(collected, varExprent);\n            if (containsLeft || !containsRight) {\n              continue;\n            }\n            return false;\n          }\n        }\n      }\n    }\n    for (Statement child : statement.getStats()) {\n      if (!checkRecursivelyAssignmentsToDelete(child, collected)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private static void collectSwitchesOn(@NotNull Statement statement,\n                                        @NotNull List<SwitchRecognizer> recognizers,\n                                        @NotNull List<SwitchOnCandidate> candidates,\n                                        @NotNull Set<SwitchStatement> usedSwitchStatement) {\n    if (statement instanceof SwitchStatement switchStatement && !usedSwitchStatement.contains(switchStatement)) {\n      SwitchExprent switchExprent = (SwitchExprent)switchStatement.getHeadExprent();\n      Exprent switchSelector = Objects.requireNonNull(switchExprent).getValue();\n      if (switchSelector instanceof InvocationExprent) {\n        for (SwitchRecognizer recognizer : recognizers) {\n          SwitchOnCandidate switchCandidate = recognizer.recognize(switchStatement, (InvocationExprent)switchSelector);\n          if (switchCandidate == null) continue;\n          candidates.add(0, switchCandidate);\n          usedSwitchStatement.addAll(switchCandidate.usedSwitch());\n          break;\n        }\n      }\n    }\n    for (Statement child : statement.getStats()) {\n      collectSwitchesOn(child, recognizers, candidates, usedSwitchStatement);\n    }\n  }\n\n  @NotNull\n  private static Map<Exprent, Exprent> evaluateCaseLabelsToFieldsMapping(@NotNull List<List<Exprent>> caseValues,\n                                                                         @NotNull ArrayExprent array) {\n    Map<Exprent, Exprent> mapping = new HashMap<>(caseValues.size());\n    if (array.getArray().type == Exprent.EXPRENT_FIELD) { // Javac compiler\n      FieldExprent arrayField = (FieldExprent)array.getArray();\n      ClassesProcessor.ClassNode classNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(arrayField.getClassname());\n      if (classNode == null) return mapping;\n      MethodWrapper wrapper = classNode.getWrapper().getMethodWrapper(CLINIT_NAME, \"()V\");\n      if (wrapper != null && wrapper.root != null) {\n        wrapper.getOrBuildGraph().iterateExprents(exprent -> {\n          if (exprent instanceof AssignmentExprent assignment) {\n            Exprent left = assignment.getLeft();\n            if (left.type == Exprent.EXPRENT_ARRAY && ((ArrayExprent)left).getArray().equals(arrayField)) {\n              mapping.put(assignment.getRight(), ((InvocationExprent)((ArrayExprent)left).getIndex()).getInstance());\n            }\n          }\n          return 0;\n        });\n      }\n    }\n    else if (array.getArray().type == Exprent.EXPRENT_INVOCATION) { // Eclipse compiler\n      InvocationExprent invocationExprent = (InvocationExprent)array.getArray();\n      ClassesProcessor.ClassNode classNode =\n        DecompilerContext.getClassProcessor().getMapRootClasses().get(invocationExprent.getClassName());\n      if (classNode == null) return mapping;\n      MethodWrapper wrapper = classNode.getWrapper().getMethodWrapper(invocationExprent.getName(), \"()[I\");\n      if (wrapper != null && wrapper.root != null) {\n        wrapper.getOrBuildGraph().iterateExprents(exprent -> {\n          if (exprent instanceof AssignmentExprent assignment) {\n            Exprent left = assignment.getLeft();\n            if (left.type == Exprent.EXPRENT_ARRAY) {\n              Exprent indexExprent = ((ArrayExprent)left).getIndex();\n              if (indexExprent.type == Exprent.EXPRENT_INVOCATION && ((InvocationExprent)indexExprent).getName().equals(\"ordinal\")) {\n                mapping.put(assignment.getRight(), ((InvocationExprent)((ArrayExprent)left).getIndex()).getInstance());\n              }\n            }\n          }\n          return 0;\n        });\n      }\n    }\n    return mapping;\n  }\n\n  @Nullable\n  private static List<List<@Nullable Exprent>> findRealCaseValues(@NotNull List<List<Exprent>> caseValues,\n                                                                  @NotNull Map<Exprent, Exprent> mapping) {\n    List<List<@Nullable Exprent>> result = new ArrayList<>(caseValues.size());\n    for (List<Exprent> caseValue : caseValues) {\n      List<@Nullable Exprent> values = new ArrayList<>(caseValue.size());\n      result.add(values);\n      for (Exprent exprent : caseValue) {\n        if (exprent == null) {\n          values.add(null);\n        }\n        else {\n          Exprent realConst = mapping.get(exprent);\n          if (realConst == null) {\n            DecompilerContext.getLogger()\n              .writeMessage(\"Unable to simplify switch on enum: \" + exprent + \" not found, available: \" + mapping, Severity.ERROR);\n            return null;\n          }\n          values.add(realConst.copy());\n        }\n      }\n    }\n    return result;\n  }\n\n  private static boolean isEnumArray(Exprent exprent) {\n    if (!(exprent instanceof ArrayExprent)) return false;\n    Exprent field = ((ArrayExprent)exprent).getArray();\n    Exprent index = ((ArrayExprent)exprent).getIndex();\n    boolean isJavacEnumArray = field instanceof FieldExprent &&\n                               (((FieldExprent)field).getName().startsWith(\"$SwitchMap\") ||\n                                (index instanceof InvocationExprent && ((InvocationExprent)index).getName().equals(\"ordinal\")));\n    boolean isEclipseEnumArray = field instanceof InvocationExprent &&\n                                 ((InvocationExprent)field).getName().startsWith(\"$SWITCH_TABLE\");\n    return isJavacEnumArray || isEclipseEnumArray;\n  }\n\n  record TempVarAssignmentItem(@NotNull VarExprent varExprent, @NotNull Statement statement) {\n\n  }\n\n  static void removeTempVariableDeclarations(@NotNull List<TempVarAssignmentItem> tempVarAssignments) {\n    if (tempVarAssignments.isEmpty()) return;\n    Set<Statement> visited = new HashSet<>();\n    Set<Statement> statements = tempVarAssignments.stream().map(a -> a.statement()).collect(Collectors.toSet());\n    Map<VarExprent, List<VarExprent>> vars = tempVarAssignments.stream().map(a -> a.varExprent()).collect(Collectors.groupingBy(t -> t));\n    for (Statement statement : statements) {\n      Statement parent = statement;\n      while (parent != null) {\n        if (visited.contains(parent)) {\n          break;\n        }\n        visited.add(parent);\n        List<Exprent> candidates;\n        if (parent.getFirst() != null && parent.getFirst().type == StatementType.BASIC_BLOCK) {\n          candidates = parent.getFirst().getExprents();\n        }\n        else if (parent.type == StatementType.TRY_CATCH || parent.type == StatementType.SEQUENCE) {\n          candidates = parent.getVarDefinitions();\n        }\n        else {\n          candidates = Collections.emptyList();\n        }\n        if (candidates == null) {\n          candidates = new ArrayList<>();\n        }\n        List<List<Exprent>> listVarExprents = new ArrayList<>();\n        listVarExprents.add(candidates);\n        if (parent.getExprents() != null) {\n          listVarExprents.add(parent.getExprents());\n        }\n        List<Exprent> toDelete = new ArrayList<>();\n        for (List<Exprent> varExprents : listVarExprents) {\n          for (int i = 0; i < varExprents.size(); i++) {\n            Exprent exprent = varExprents.get(i);\n            Exprent assignmentExprent = null;\n            if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n              assignmentExprent = exprent;\n              exprent = ((AssignmentExprent)exprent).getLeft();\n            }\n            if (exprent.type != Exprent.EXPRENT_VAR) continue;\n            VarExprent varExprent = (VarExprent)exprent;\n            if (containVar(vars, varExprent) || (varExprent.isDefinition() && vars.containsKey(varExprent))) {\n              toDelete.add(assignmentExprent == null ? varExprent : assignmentExprent);\n            }\n          }\n          varExprents.removeAll(toDelete);\n        }\n        parent = parent.getParent();\n      }\n    }\n  }\n\n  private static boolean containVar(@NotNull Map<VarExprent, List<VarExprent>> vars, @Nullable Exprent exprent) {\n    if (exprent == null) {\n      return false;\n    }\n    List<VarExprent> exprents = vars.get(exprent);\n    if (exprents == null) {\n      return false;\n    }\n    for (VarExprent varExprent : exprents) {\n      if (exprent == varExprent) {\n        //exactly the same reference\n        return true;\n      }\n    }\n    return false;\n  }\n\n  public static void prepareForRules(@NotNull Statement statement, @NotNull StructClass cl) {\n    if (!cl.hasEnhancedSwitchSupport()) {\n      return;\n    }\n    if (statement instanceof SwitchStatement switchStatement) {\n      if (canBeRules(switchStatement)) {\n        switchStatement.setCanBeRule(true);\n        prepareForRules(switchStatement);\n      }\n    }\n    for (Statement child : statement.getStats()) {\n      prepareForRules(child, cl);\n    }\n  }\n\n  private static boolean canBeRules(@NotNull SwitchStatement statement) {\n    if (statement.isLabeled()) {\n      return false;\n    }\n    //only for simplification\n    for (List<Exprent> value : statement.getCaseValues()) {\n      if (value.size() != 1) {\n        return false;\n      }\n    }\n\n    for (Statement caseStatement : statement.getCaseStatements()) {\n      //only for simplification and not to create long rules\n      if (caseStatement instanceof SequenceStatement || !caseStatement.getStats().isEmpty() ||\n          caseStatement.getExprents() != null &&\n          caseStatement.getExprents().size() > 1) {\n        return false;\n      }\n      List<StatEdge> successorEdges = caseStatement.getSuccessorEdges(EdgeType.DIRECT_ALL);\n      if (successorEdges.size() != 1) {\n        return false;\n      }\n      StatEdge edge = successorEdges.get(0);\n      if (edge.getType() != EdgeType.BREAK) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private static void prepareForRules(@NotNull SwitchStatement statement) {\n    for (Statement caseStatement : statement.getCaseStatements()) {\n      List<StatEdge> successorEdges = caseStatement.getSuccessorEdges(EdgeType.DIRECT_ALL);\n      if (successorEdges.size() != 1) {\n        continue;\n      }\n      StatEdge edge = successorEdges.get(0);\n      if (edge.getType() == EdgeType.BREAK && edge.explicit && !edge.labeled) {\n        edge.explicit = false;\n      }\n    }\n  }\n\n  interface SwitchRecognizer {\n    @Nullable\n    SwitchOnCandidate recognize(@NotNull SwitchStatement statement, @NotNull InvocationExprent switchSelector);\n  }\n\n  private abstract static class StringSwitchRecognizer implements SwitchRecognizer {\n\n    @Override\n    @Nullable\n    public abstract SwitchOnStringCandidate recognize(@NotNull SwitchStatement statement, @NotNull InvocationExprent switchSelector);\n\n    @NotNull\n    Set<Object> findRealCaseValuesHashCodes(@NotNull SwitchStatement switchStatement) {\n      // noinspection SSBasedInspection\n      return switchStatement.getCaseValues().stream()\n        // we take only buckets that don't contain null value.\n        // Null value represents default branch and no temp variable is assigned there.\n        // Also, the bucket with null value may also contain fictive case values,\n        // generated for tableswitch instruction (see com.sun.tools.javac.jvm.Gen.visitSwitch)\n        .filter(values -> values.stream().noneMatch(Objects::isNull)).flatMap(Collection::stream)\n        .map(value -> ((ConstExprent)value)).map(ConstExprent::getValue)\n        .collect(Collectors.toSet());\n    }\n\n    @Nullable\n    String findRealCaseValue(@NotNull IfStatement ifStatement, @NotNull Exprent selectorQualifier) {\n      Exprent ifCondition = ifStatement.getHeadexprent().getCondition();\n      if (ifCondition.type != Exprent.EXPRENT_INVOCATION) return null;\n      InvocationExprent invocationCondition = (InvocationExprent)ifCondition;\n      if (!invocationCondition.isInstanceCall(ClassNameConstants.JAVA_LANG_STRING, \"equals\", 1)) return null;\n      if (!invocationCondition.getInstance().equals(selectorQualifier)) return null;\n      Exprent equalsParameter = invocationCondition.getParameters().get(0);\n      if (equalsParameter.type != Exprent.EXPRENT_CONST) return null;\n      Object caseLabelValue = ((ConstExprent)equalsParameter).getValue();\n      // We take hash code of case label value for comparing, as javac uses this strategy to generate the first switch statement.\n      // Seems Ecj uses the same strategy.\n      //\n      // From javac doc:\n      // The generated code assumes that the hashing algorithm\n      // of String is the same in the compilation environment as\n      // in the environment the code will run in.  The string\n      // hashing algorithm in the SE JDK has been unchanged\n      // since at least JDK 1.2.  Since the algorithm has been\n      // specified since that release as well, it is very\n      // unlikely to be changed in the future.\n      return caseLabelValue instanceof String ? (String)caseLabelValue : null;\n    }\n\n    private static class JavacStringRecognizer extends StringSwitchRecognizer {\n      /**\n       * From javac doc:\n       * The general approach used is to translate a single\n       * string switch statement into a series of two chained\n       * switch statements: the first a synthesized statement\n       * switching on the argument string's hash value and\n       * computing a string's position in the list of original\n       * case labels, if any, followed by a second switch on the\n       * computed integer value.  The second switch has the same\n       * code structure as the original string switch statement\n       * except that the string case labels are replaced with\n       * positional integer constants starting at 0.\n       *\n       * @param firstSwitch    outer or first switch\n       * @param switchSelector <code>firstSwitch</code> selector\n       * @return recognized a switch candidate\n       * see Lower.visitStringSwitch(JCTree.JCSwitch) in javac\n       */\n      @Nullable\n      @Override\n      public SwitchOnStringCandidate recognize(@NotNull SwitchStatement firstSwitch,\n                                               @NotNull InvocationExprent switchSelector) {\n        if (switchSelector.getInstance() == null || switchSelector.getInstance().type != Exprent.EXPRENT_VAR) return null;\n        if (!switchSelector.isInstanceCall(ClassNameConstants.JAVA_LANG_STRING, \"hashCode\", 0)) return null;\n\n        Set<Object> realCaseValueHashCodes = findRealCaseValuesHashCodes(firstSwitch);\n        VarExprent firstSwitchSelectorQualifier = (VarExprent)switchSelector.getInstance();\n        VarExprent tmpVarAssignTo = null;\n        SwitchStatement secondSwitch = null;\n        Map<Integer, String> mappedCaseLabelValues = new HashMap<>();\n        for (Statement statement : firstSwitch.getCaseStatements()) {\n          if (statement.type != StatementType.IF) {\n            Statement defaultStatement = firstSwitch.getDefaultEdge().getDestination();\n            if (defaultStatement != statement) return null;\n            continue;\n          }\n          IfStatement ifStatement = (IfStatement)statement;\n          String caseLabelValue = findRealCaseValue(ifStatement, firstSwitchSelectorQualifier);\n          if (caseLabelValue == null) return null;\n          if (!realCaseValueHashCodes.remove(caseLabelValue.hashCode())) return null;\n          if (ifStatement.getIfstat() == null) return null;\n          List<Exprent> ifStatementExprents = ifStatement.getIfstat().getExprents();\n          if (ifStatementExprents == null || ifStatementExprents.size() != 1) return null;\n          if (ifStatementExprents.get(0).type != Exprent.EXPRENT_ASSIGNMENT) return null;\n          AssignmentExprent assignment = (AssignmentExprent)ifStatementExprents.get(0);\n          if (assignment.getLeft().type != Exprent.EXPRENT_VAR || assignment.getRight().type != Exprent.EXPRENT_CONST) return null;\n          // tmp variable should be the same for all assignments\n          if (tmpVarAssignTo != null && !tmpVarAssignTo.equals(assignment.getLeft())) return null;\n          tmpVarAssignTo = (VarExprent)assignment.getLeft();\n          Object valueAssignedToTmpVar = ((ConstExprent)assignment.getRight()).getValue();\n          if (!(valueAssignedToTmpVar instanceof Integer)) return null;\n          mappedCaseLabelValues.put((Integer)valueAssignedToTmpVar, caseLabelValue);\n\n          if (ifStatement.getLabelEdges().size() != 1) return null;\n          Statement edgeDestination = ifStatement.getLabelEdges().iterator().next().getDestination();\n          if (edgeDestination.type == StatementType.SEQUENCE) {\n            edgeDestination = edgeDestination.getFirst();\n          }\n          if (edgeDestination.type != StatementType.SWITCH) return null;\n          // the switch should be the same for all case labels\n          if (secondSwitch != null && secondSwitch != edgeDestination) return null;\n          secondSwitch = (SwitchStatement)edgeDestination;\n        }\n        if (secondSwitch == null || !realCaseValueHashCodes.isEmpty()) return null;\n\n        Exprent siblingSwitchExprent = secondSwitch.getHeadExprent();\n        if (siblingSwitchExprent == null) return null;\n        if (siblingSwitchExprent.type != Exprent.EXPRENT_SWITCH) return null;\n        if (!tmpVarAssignTo.equals(((SwitchExprent)siblingSwitchExprent).getValue())) return null;\n\n        Exprent secondSwitchSelector = secondSwitch.getHeadExprent();\n        if (secondSwitchSelector == null || secondSwitchSelector.type != Exprent.EXPRENT_SWITCH) return null;\n        Statement firstStatementInFirstSwitch = firstSwitch.getFirst();\n        if (firstStatementInFirstSwitch.type != StatementType.BASIC_BLOCK) return null;\n        Statement firstStatementInSecondSwitch = secondSwitch.getFirst();\n        if (firstStatementInSecondSwitch.type != StatementType.BASIC_BLOCK) return null;\n        List<Exprent> firstSwitchExprents = firstStatementInFirstSwitch.getExprents();\n        if (firstSwitchExprents == null || firstStatementInSecondSwitch.getExprents() == null) return null;\n\n        return new SwitchOnStringCandidate.JavacSwitchCandidate(firstSwitch, firstSwitchSelectorQualifier, tmpVarAssignTo, secondSwitch,\n                                                                mappedCaseLabelValues,\n                                                                firstSwitchExprents,\n                                                                secondSwitchSelector,\n                                                                firstStatementInSecondSwitch);\n      }\n    }\n\n    private static class EcjStringRecognizer extends StringSwitchRecognizer {\n      @Nullable\n      @Override\n      public SwitchOnStringCandidate recognize(@NotNull SwitchStatement switchStatement, @NotNull InvocationExprent switchSelector) {\n        if (!switchSelector.isInstanceCall(ClassNameConstants.JAVA_LANG_STRING, \"hashCode\", 0)) return null;\n        Set<Object> realCaseValueHashCodes = findRealCaseValuesHashCodes(switchStatement);\n        Exprent switchSelectorQualifier = switchSelector.getInstance();\n        Map<Integer, String> mappedCaseLabelValues = new HashMap<>();\n        Map<Integer, IfStatement> ifBodyStatements = new HashMap<>();\n        VarExprent tempVar = null;\n        for (Statement statement : switchStatement.getCaseStatements()) {\n          if (statement.type != StatementType.IF) {\n            Statement defaultStatement = switchStatement.getDefaultEdge().getDestination();\n            if (defaultStatement != statement) return null;\n            continue;\n          }\n          Exprent tempSwitchSelectorQualifier = switchSelectorQualifier;\n          if (switchSelectorQualifier.type == Exprent.EXPRENT_ASSIGNMENT) {\n            tempSwitchSelectorQualifier = ((AssignmentExprent)switchSelectorQualifier).getLeft();\n          }\n          else if (switchSelectorQualifier.type == Exprent.EXPRENT_CONST && switchStatement.getFirst().getExprents() != null) {\n            // Ecj inlines compile constants, so we try to find a temp variable assigned to the same const value.\n            // It should be in the first basic block.\n            Exprent finalSwitchSelectorQualifier = switchSelectorQualifier;\n            tempSwitchSelectorQualifier = switchStatement.getFirst().getExprents().stream()\n              .filter(exprent -> exprent instanceof AssignmentExprent)\n              .map(exprent -> (AssignmentExprent)exprent)\n              .filter(exprent -> exprent.getRight().equals(finalSwitchSelectorQualifier))\n              .map(AssignmentExprent::getLeft)\n              .findFirst()\n              .orElse(null);\n            if (tempSwitchSelectorQualifier == null) return null;\n          }\n          if (tempVar != null && !tempVar.equals(tempSwitchSelectorQualifier)) return null;\n          tempVar = (VarExprent)tempSwitchSelectorQualifier;\n          IfStatement ifStatement = (IfStatement)statement;\n          String caseLabelValue = findRealCaseValue(ifStatement, tempVar);\n          if (caseLabelValue == null) return null;\n          int caseLabelHash = caseLabelValue.hashCode();\n          if (!realCaseValueHashCodes.remove(caseLabelHash)) return null;\n          mappedCaseLabelValues.put(caseLabelHash, caseLabelValue);\n          ifBodyStatements.put(caseLabelHash, ifStatement);\n        }\n        if (tempVar == null || !realCaseValueHashCodes.isEmpty()) return null;\n        if (switchSelectorQualifier instanceof AssignmentExprent) {\n          switchSelectorQualifier = ((AssignmentExprent)switchSelectorQualifier).getRight();\n        }\n        return new SwitchOnStringCandidate.EcjSwitchCandidate(switchStatement, switchSelectorQualifier, tempVar, ifBodyStatements,\n                                                              mappedCaseLabelValues);\n      }\n    }\n  }\n\n  interface SwitchOnCandidate {\n    void simplify();\n\n    Set<SwitchStatement> usedSwitch();\n\n    List<TempVarAssignmentItem> prepareTempAssignments();\n  }\n\n  private abstract static class SwitchOnStringCandidate implements SwitchOnCandidate {\n    @Nullable\n    private static AssignmentExprent addTempVarAssignment(@NotNull Exprent exprent,\n                                                          @NotNull VarExprent varExprent,\n                                                          @NotNull Statement statement,\n                                                          @NotNull List<TempVarAssignmentItem> tempVarAssignments) {\n      if (exprent.type != Exprent.EXPRENT_ASSIGNMENT) return null;\n      AssignmentExprent assignment = (AssignmentExprent)exprent;\n      if (!varExprent.isDefinition() && varExprent.equals(assignment.getLeft())) {\n        tempVarAssignments.add(new TempVarAssignmentItem(varExprent, statement));\n        return assignment;\n      }\n      return null;\n    }\n\n    private static class JavacSwitchCandidate extends SwitchOnStringCandidate {\n      @NotNull private final SwitchStatement firstSwitch;\n      @NotNull private final SwitchStatement secondSwitch;\n      @NotNull private final VarExprent firstSwitchSelector;\n      @NotNull private final Exprent secondSwitchSelector;\n      @NotNull private final VarExprent tmpVarInFirstSwitch;\n      @NotNull private final Map<Integer, @NotNull String> mappedCaseLabelValues;\n      @NotNull private final List<Exprent> firstSwitchExprents;\n      @NotNull private final Statement firstStatementInSecondSwitch;\n      @NotNull private final NewSelector newSelector;\n      @NotNull private final List<TempVarAssignmentItem> tempVarAssignments = new ArrayList<>();\n\n      JavacSwitchCandidate(@NotNull SwitchStatement firstSwitch,\n                           @NotNull VarExprent firstSwitchSelector,\n                           @NotNull VarExprent tmpVarInFirstSwitch,\n                           @NotNull SwitchStatement secondSwitch,\n                           @NotNull Map<Integer, String> mappedCaseLabelValues,\n                           @NotNull List<Exprent> firstSwitchExprents,\n                           @NotNull Exprent secondSwitchSelector,\n                           @NotNull Statement firstStatementInSecondSwitch) {\n        this.firstSwitch = firstSwitch;\n        this.secondSwitch = secondSwitch;\n        this.firstSwitchSelector = firstSwitchSelector;\n        this.secondSwitchSelector = secondSwitchSelector;\n\n        this.tmpVarInFirstSwitch = tmpVarInFirstSwitch;\n        this.mappedCaseLabelValues = mappedCaseLabelValues;\n\n        this.firstSwitchExprents = firstSwitchExprents;\n        this.firstStatementInSecondSwitch = firstStatementInSecondSwitch;\n\n        newSelector = getSelector(tempVarAssignments, firstSwitchExprents);\n      }\n\n      @Override\n      public void simplify() {\n\n        for (List<Exprent> values : secondSwitch.getCaseValues()) {\n          for (int i = 0; i < values.size(); i++) {\n            ConstExprent constExprent = (ConstExprent)values.get(i);\n            if (constExprent == null) continue;\n            String labelValue = mappedCaseLabelValues.get(constExprent.getIntValue());\n            values.set(i, new ConstExprent(VARTYPE_STRING, labelValue, null));\n          }\n        }\n\n        List<Exprent> newExprents;\n        if (newSelector.lastExprentIndex() > 0) {\n          newExprents = List.copyOf(firstSwitchExprents.subList(0, newSelector.lastExprentIndex()));\n        }\n        else {\n          newExprents = Collections.emptyList();\n        }\n        Statement firstSwitchParent = firstSwitch.getParent();\n        Statement secondSwitchParent = secondSwitch.getParent();\n        boolean parentsAreTheSame = firstSwitchParent == secondSwitchParent;\n        if (parentsAreTheSame || secondSwitchParent == firstSwitch) {\n          firstSwitchParent.replaceStatement(firstSwitch, secondSwitch);\n        }\n        else if (secondSwitchParent.getParent() == firstSwitch) {\n          firstSwitchParent.replaceStatement(firstSwitch, secondSwitchParent);\n        }\n        if (parentsAreTheSame) {\n          firstSwitchParent.getStats().removeWithKey(secondSwitch.id);\n        }\n        List<Exprent> exprents = firstStatementInSecondSwitch.getExprents();\n        if (exprents != null) {\n          exprents.addAll(0, newExprents);\n        }\n        secondSwitchSelector.replaceExprent(((SwitchExprent)secondSwitchSelector).getValue(), newSelector.newSelector());\n      }\n\n      @Override\n      public Set<SwitchStatement> usedSwitch() {\n        return Set.of(firstSwitch, secondSwitch);\n      }\n\n      @NotNull\n      private NewSelector getSelector(@NotNull List<TempVarAssignmentItem> tempVarAssignments, List<Exprent> firstSwitchExprents) {\n        int lastExprentIndex = firstSwitchExprents.size();\n        if (lastExprentIndex > 0) {\n          AssignmentExprent assignment = addTempVarAssignment(firstSwitchExprents.get(lastExprentIndex - 1), tmpVarInFirstSwitch,\n                                                              secondSwitch, tempVarAssignments);\n          if (assignment != null) {\n            lastExprentIndex--;\n          }\n        }\n        Exprent newSelector = firstSwitchSelector;\n        if (lastExprentIndex > 0) {\n          AssignmentExprent assignment = addTempVarAssignment(firstSwitchExprents.get(lastExprentIndex - 1), firstSwitchSelector,\n                                                              secondSwitch, tempVarAssignments);\n          if (assignment != null) {\n            lastExprentIndex--;\n            newSelector = assignment.getRight();\n          }\n        }\n        return new NewSelector(lastExprentIndex, newSelector);\n      }\n\n      @Override\n      public List<TempVarAssignmentItem> prepareTempAssignments() {\n        return tempVarAssignments;\n      }\n\n      private record NewSelector(int lastExprentIndex, Exprent newSelector) {\n      }\n    }\n\n    private static class EcjSwitchCandidate extends SwitchOnStringCandidate {\n      @NotNull private final SwitchStatement switchStatement;\n      @NotNull private final Exprent switchSelector;\n      @NotNull private final Map<Integer, @NotNull IfStatement> mappedIfStatements;\n      @NotNull private final Map<Integer, @NotNull String> mappedCaseLabelValues;\n      @NotNull private final List<TempVarAssignmentItem> tempVarAssignments = new ArrayList<>();\n\n      private EcjSwitchCandidate(@NotNull SwitchStatement switchStatement,\n                                 @NotNull Exprent switchSelector,\n                                 @NotNull VarExprent tmpVar,\n                                 @NotNull Map<Integer, IfStatement> mappedIfStatements,\n                                 @NotNull Map<Integer, String> mappedCaseLabelValues) {\n        this.switchStatement = switchStatement;\n        this.switchSelector = switchSelector;\n        this.mappedIfStatements = mappedIfStatements;\n        this.mappedCaseLabelValues = mappedCaseLabelValues;\n        this.tempVarAssignments.add(new TempVarAssignmentItem(tmpVar, switchStatement));\n      }\n\n      @Override\n      public void simplify() {\n        Exprent switchSelector = switchStatement.getHeadExprent();\n        if (switchSelector == null || switchSelector.type != Exprent.EXPRENT_SWITCH) return;\n        for (List<Exprent> values : switchStatement.getCaseValues()) {\n          for (int i = 0; i < values.size(); i++) {\n            ConstExprent constExprent = (ConstExprent)values.get(i);\n            if (constExprent == null) continue;\n            int caseLabelHash = constExprent.getIntValue();\n            String labelValue = mappedCaseLabelValues.get(caseLabelHash);\n            values.set(i, new ConstExprent(VARTYPE_STRING, labelValue, null));\n            IfStatement ifStatement = mappedIfStatements.get(caseLabelHash);\n            assert !ifStatement.getStats().isEmpty();\n            if (ifStatement.getStats().size() == 1) {\n              ifStatement.getParent().replaceStatement(ifStatement, ifStatement.getStats().get(0));\n              continue;\n            }\n            removeOuterBreakEdge(ifStatement);\n            ifStatement.getParent().replaceStatement(ifStatement, new SequenceStatement(ifStatement.getStats()));\n          }\n        }\n        switchSelector.replaceExprent(((SwitchExprent)switchSelector).getValue(), this.switchSelector);\n      }\n\n      @Override\n      public Set<SwitchStatement> usedSwitch() {\n        return Set.of(switchStatement);\n      }\n\n      @Override\n      public List<TempVarAssignmentItem> prepareTempAssignments() {\n        return tempVarAssignments;\n      }\n\n      private static void removeOuterBreakEdge(@NotNull IfStatement ifStatement) {\n        List<StatEdge> ifStatementBreakEdges = ifStatement.getSuccessorEdges(EdgeType.BREAK);\n        if (ifStatementBreakEdges.size() != 1) return;\n        Statement lastStatement = ifStatement.getStats().get(ifStatement.getStats().size() - 1);\n        List<StatEdge> lastStatementBreakEdges = lastStatement.getSuccessorEdges(EdgeType.BREAK);\n        if (lastStatementBreakEdges.size() != 1) return;\n        StatEdge firstIfStatementBreakEdge = ifStatementBreakEdges.get(0);\n        StatEdge lastStatementBreakEdge = lastStatementBreakEdges.get(0);\n        if (firstIfStatementBreakEdge.getDestination() != lastStatementBreakEdge.getDestination()) {\n          ifStatement.removeSuccessor(firstIfStatementBreakEdge);\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/SwitchPatternHelper.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.struct.consts.PooledConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport static org.jetbrains.java.decompiler.ClassNameConstants.JAVA_LANG_OBJECT;\nimport static org.jetbrains.java.decompiler.ClassNameConstants.JAVA_UTIL_OBJECTS;\nimport static org.jetbrains.java.decompiler.modules.decompiler.PatternHelper.*;\nimport static org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper.SwitchOnCandidate;\nimport static org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper.TempVarAssignmentItem;\nimport static org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent.EXIT_THROW;\nimport static org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement.IFTYPE_IF;\nimport static org.jetbrains.java.decompiler.struct.gen.VarType.*;\n\n/**\n * The SwitchPatternHelper class provides utility methods to work with switch statement patterns.\n */\n@SuppressWarnings(\"SSBasedInspection\")\npublic final class SwitchPatternHelper {\n\n  public static boolean isBootstrapSwitch(@NotNull Exprent headExprent) {\n    if (!(headExprent instanceof SwitchExprent switchExprent)) {\n      return false;\n    }\n    Exprent switchSelector = switchExprent.getValue();\n    if (switchSelector instanceof InvocationExprent invocationExprent) {\n      if (invocationExprent.isDynamicCall(\"typeSwitch\", 2)) return true;\n      if (invocationExprent.isDynamicCall(\"enumSwitch\", 2)) return true;\n    }\n    return false;\n  }\n\n  /**\n   * A class that implements the SwitchHelper.SwitchRecognizer interface and provides a method to recognize a switch statement.\n   *\n   * @see JavacReferenceFinder for details\n   */\n  static class JavacReferenceRecognizer implements SwitchHelper.SwitchRecognizer {\n\n    @Override\n    public @Nullable SwitchOnCandidate recognize(@NotNull SwitchStatement statement, @NotNull InvocationExprent switchSelector) {\n      return new JavacReferenceFinder(null, new HashSet<>(), statement, switchSelector).findCandidate();\n    }\n  }\n\n  /**\n   * The JavacReferenceFinder class is responsible for finding reference candidates for a switch statement with patterns in bytecode.\n   * This finder tries to find only javac familiar structures for patterns.\n   * For deconstructions, only common structures are trying to be found; some cases are left\n   */\n  private static class JavacReferenceFinder {\n\n    /**\n     * The {@code FullCase} class represents a case in a switch statement. It contains the statement,\n     * the list of expressions (exprents), and the list of statement edges related to the case.\n     *\n     * @see SwitchStatement\n     */\n    private record FullCase(@NotNull Statement statement, @NotNull List<Exprent> exprents, @NotNull List<StatEdge> edges) {\n    }\n\n    /**\n     * Represents a case value with an associated edge from a switch statement\n     *\n     * @param exprent The expression value of the case.\n     * @param edge    The edge associated with the case.\n     * @see SwitchStatement\n     */\n    private record CaseValueWithEdge(@Nullable Exprent exprent, @Nullable StatEdge edge) {\n    }\n\n\n    /**\n     * Represents the root DoStatement and exprents, which contain initializers.\n     *\n     * @param firstStatement    The firstStatement statement.\n     * @param firstExprents     The list of expression statements.\n     * @param doParentStatement The parent DoStatement object.\n     */\n    private record Root(Statement firstStatement, List<Exprent> firstExprents, DoStatement doParentStatement) {\n    }\n\n    /**\n     * not null for nested switch\n     */\n    @Nullable\n    private final VarTracker myVarTracker;\n\n    /**\n     * Represents a set of type variables, which is used as second parameter in bootstrap call.\n     */\n    @NotNull\n    private final Set<Exprent> myTypeVars;\n    @NotNull\n    private final SwitchStatement myRootSwitchStatement;\n    @NotNull\n    private final InvocationExprent mySwitchSelector;\n\n    @NotNull\n    private final List<TempVarAssignmentItem> myTempVarAssignments = new ArrayList<>();\n\n    @NotNull\n    private final Set<SwitchStatement> myUsedSwitch = new HashSet<>();\n\n    private JavacReferenceFinder(@Nullable VarTracker tracker,\n                                 @NotNull Set<Exprent> typeVars,\n                                 @NotNull SwitchStatement rootSwitchStatement,\n                                 @NotNull InvocationExprent switchSelector) {\n      this.myVarTracker = tracker;\n      this.myTypeVars = new HashSet<>(typeVars);\n      this.myRootSwitchStatement = rootSwitchStatement;\n      this.mySwitchSelector = switchSelector;\n      myUsedSwitch.add(myRootSwitchStatement);\n    }\n\n    public @Nullable SwitchOnReferenceCandidate findCandidate() {\n      Exprent instance = mySwitchSelector.getInstance();\n      if (instance == null) {\n        return null;\n      }\n      if (myRootSwitchStatement.getHeadExprent() == null) {\n        return null;\n      }\n\n      if (myRootSwitchStatement.getCaseValues().size() != myRootSwitchStatement.getCaseEdges().size() ||\n          myRootSwitchStatement.getCaseValues().size() != myRootSwitchStatement.getCaseStatements().size()) {\n        return null;\n      }\n\n      if (!(instance instanceof VarExprent instanceVarExprent)) return null;\n      if (!isBootstrapSwitch(myRootSwitchStatement.getHeadExprent())) return null;\n      if (checkBootstrap()) return null;\n\n      List<Exprent> parameters = mySwitchSelector.getParameters();\n      if (!instance.equals(parameters.get(0))) {\n        return null;\n      }\n      Exprent typeVar = parameters.get(1);\n      Root root = getRoot();\n      if (root == null) return null;\n\n      if (root.firstExprents() == null) {\n        return null;\n      }\n\n      if (myVarTracker != null) {\n        processAtLeastOneBlock(myVarTracker, root.firstStatement());\n      }\n\n      Initializer initializer = findInitializer(root, typeVar);\n\n      if (initializer.initVar2() == null) {\n        return null;\n      }\n\n      Set<AssignmentExprent> usedTypeVarAssignments = new HashSet<>();\n      usedTypeVarAssignments.add(initializer.initVar2());\n      myTypeVars.add(typeVar);\n\n      Map<Statement, List<PatternVariableCandidate>> candidates =\n        collectPatterns(instanceVarExprent, root.doParentStatement(), usedTypeVarAssignments);\n      if (candidates == null) {\n        return null;\n      }\n      candidates.values()\n        .stream()\n        .flatMap(t -> t.stream())\n        .forEach(candidate -> myTempVarAssignments.addAll(candidate.getTempAssignments()));\n      PatternContainer patternContainer = collectGuards(candidates, typeVar, root.doParentStatement(), usedTypeVarAssignments);\n      if (root.doParentStatement() != null) {\n        if (checkReinitVar(typeVar, root.doParentStatement(), usedTypeVarAssignments)) {\n          return null;\n        }\n      }\n      else {\n        if (checkReinitVar(typeVar, myRootSwitchStatement, usedTypeVarAssignments)) {\n          return null;\n        }\n      }\n\n\n      List<FullCase> resortedCases = resortForSwitchBootstrap(myRootSwitchStatement);\n      if (resortedCases == null) {\n        return null;\n      }\n\n      List<Exprent> finalFirstExprents = root.firstExprents();\n      Exprent finalNonNullCheck = initializer.nonNullCheck();\n\n      return new SwitchOnReferenceCandidate(myRootSwitchStatement, mySwitchSelector, initializer.instance(), resortedCases,\n                                            patternContainer,\n                                            myTempVarAssignments, myUsedSwitch,\n                                            usedTypeVarAssignments, root.doParentStatement(),\n                                            initializer.nonNullCheck() != null, () -> {\n        if (finalNonNullCheck != null) {\n          finalFirstExprents.remove(finalNonNullCheck);\n        }\n      });\n    }\n\n    @NotNull\n    private SwitchPatternHelper.JavacReferenceFinder.Initializer findInitializer(@NotNull Root root, Exprent typeVar) {\n      Exprent instance = Objects.requireNonNull(mySwitchSelector.getInstance());\n\n      Exprent nonNullCheck = null;\n      AssignmentExprent initVar2 = null;\n\n      for (Exprent exprent : root.firstExprents()) {\n        if (exprent instanceof AssignmentExprent firstAssignment && instance.equals(firstAssignment.getLeft())) {\n          instance = firstAssignment.getRight();\n          if (firstAssignment.getLeft() instanceof VarExprent varExprent) {\n            myTempVarAssignments.add(new TempVarAssignmentItem(varExprent, root.firstStatement()));\n          }\n        }\n      }\n      for (Exprent exprent : root.firstExprents()) {\n        if (isNonNullCheck(exprent, instance)) {\n          nonNullCheck = exprent;\n        }\n        if (exprent instanceof AssignmentExprent firstAssignment && typeVar.equals(firstAssignment.getLeft()) &&\n            firstAssignment.getRight() instanceof ConstExprent constExprent &&\n            constExprent.getValue() instanceof Integer value &&\n            value == 0) {\n          initVar2 = firstAssignment;\n\n          if (firstAssignment.getLeft() instanceof VarExprent varExprent) {\n            myTempVarAssignments.add(new TempVarAssignmentItem(varExprent, root.firstStatement()));\n          }\n        }\n      }\n      return new Initializer(instance, nonNullCheck, initVar2);\n    }\n\n    /**\n     * Represents an initializer for a switch statement.\n     *\n     * @param instance     The qualifier which is used to call bootstrap methods.\n     * @param nonNullCheck non-null-check for instance.\n     * @param initVar2     second argument, which represent a type.\n     */\n    private record Initializer(Exprent instance, Exprent nonNullCheck, AssignmentExprent initVar2) {\n    }\n\n    /**\n     * This method matches the structure from myRootSwitchStatement with common patterns to find root DoStatement.\n     * DoStatement is used for pattern matching to repeat searching if a previous assumption is not correct.\n     *\n     * @return The `Root` object representing the root `DoStatement` and expression statements, or null if not found.\n     */\n    @Nullable\n    private Root getRoot() {\n      Statement first = myRootSwitchStatement.getFirst();\n      if (first == null) {\n        return null;\n      }\n      List<Exprent> firstExprents = first.getExprents();\n\n      DoStatement doParentStatement = null;\n\n      //possible structure (only for nested switch):\n      //SequenceStatement\n      //  ....\n      //  Basic\n      //  DoStatement\n      //    DoStatement\n      //      Switch\n      if (myVarTracker != null && myRootSwitchStatement.getParent() instanceof DoStatement nestedDoStatement &&\n          nestedDoStatement.getConditionExprent() == null &&\n          nestedDoStatement.getParent() instanceof DoStatement upperDoStatement &&\n          upperDoStatement.getConditionExprent() == null &&\n          upperDoStatement.getParent() instanceof SequenceStatement sequenceStatement &&\n          sequenceStatement.getStats().size() >= 2 && sequenceStatement.getStats().getLast() == upperDoStatement &&\n          sequenceStatement.getStats().get(sequenceStatement.getStats().size() - 2) instanceof BasicBlockStatement basicBlockStatement) {\n        firstExprents = basicBlockStatement.getExprents();\n        first = basicBlockStatement;\n        doParentStatement = nestedDoStatement;\n      }\n      //possible structure (only for nested switch):\n      //SequenceStatement\n      //  Switch\n      //    default\n      //    case\n      //  DoStatement\n      //    DoStatement\n      //      Switch\n      else if (myVarTracker != null && myRootSwitchStatement.getParent() instanceof DoStatement nestedDoStatement &&\n               nestedDoStatement.getConditionExprent() == null &&\n               nestedDoStatement.getParent() instanceof DoStatement upperDoStatement &&\n               upperDoStatement.getConditionExprent() == null &&\n               upperDoStatement.getParent() instanceof SequenceStatement sequenceStatement &&\n               sequenceStatement.getStats().size() == 2 && sequenceStatement.getStats().get(1) == upperDoStatement &&\n               sequenceStatement.getStats().get(0) instanceof SwitchStatement upperSwitchStatement &&\n               upperSwitchStatement.getCaseStatements().size() == 2) {\n        int indexDefault = upperSwitchStatement.getCaseEdges().get(0).contains(upperSwitchStatement.getDefaultEdge()) ? 0 :\n                           (upperSwitchStatement.getCaseEdges().get(1).contains(upperSwitchStatement.getDefaultEdge()) ? 1 : -1);\n        if (indexDefault == -1) {\n          return null;\n        }\n        int other = indexDefault == 0 ? 1 : 0;\n        Statement statement = upperSwitchStatement.getCaseStatements().get(other);\n        if (statement.getStats().isEmpty()) {\n          return null;\n        }\n\n        Statement last = statement.getStats().getLast();\n        firstExprents = last.getExprents();\n        first = last;\n        doParentStatement = nestedDoStatement;\n      }\n\n      //possible structure (main case):\n      //SequenceStatement\n      //  ....\n      //  BasicBlockStatement\n      //    exprents with init values\n      //  DoStatement\n      //    SwitchStatement\n      //  ....\n      else if (firstExprents == null || firstExprents.stream().noneMatch(t -> t instanceof AssignmentExprent)) {\n        Statement parent = myRootSwitchStatement.getParent();\n        DoStatement doStatement = null;\n        if (parent instanceof DoStatement) {\n          doStatement = (DoStatement)parent;\n        }\n        if (doStatement == null && myRootSwitchStatement.getParent().getParent() instanceof DoStatement nextStatement) {\n          doStatement = nextStatement;\n        }\n        if (doStatement == null ||\n            doStatement.getLoopType() != DoStatement.LoopType.DO ||\n            doStatement.getStats().size() != 1 ||\n            !(doStatement.getParent() instanceof SequenceStatement upperStatement) ||\n            upperStatement.getExprents() != null) {\n          return null;\n        }\n        VBStyleCollection<Statement, Integer> upperStatementStats = upperStatement.getStats();\n        int indexOfDo = upperStatementStats.indexOf(doStatement);\n        if (indexOfDo == -1 || indexOfDo == 0) {\n          return null;\n        }\n        if (upperStatementStats.get(indexOfDo) == doStatement &&\n            upperStatementStats.get(indexOfDo - 1) instanceof BasicBlockStatement basicBlockStatement &&\n            basicBlockStatement.getStats().isEmpty() &&\n            basicBlockStatement.getExprents() != null &&\n            !basicBlockStatement.getExprents().isEmpty()) {\n          firstExprents = basicBlockStatement.getExprents();\n          doParentStatement = doStatement;\n          first = basicBlockStatement;\n        }\n        //if previous is CatchStatement and initializers are inside CatchStatement\n        else if (upperStatementStats.get(indexOfDo) == doStatement &&\n                 upperStatementStats.get(indexOfDo - 1) instanceof CatchStatement catchStatement &&\n                 catchStatement.getStats().size() >= 2 &&\n                 catchStatement.getStats().get(1).getExprents() != null &&\n                 !Objects.requireNonNull(catchStatement.getStats().get(1).getExprents()).isEmpty()) {\n          firstExprents = catchStatement.getStats().get(1).getExprents();\n          doParentStatement = doStatement;\n          first = catchStatement.getStats().get(1);\n        }\n        else {\n          return null;\n        }\n      }\n\n      //check that this doParentStatement is not refered outside\n      if (doParentStatement != null) {\n        for (StatEdge edge : doParentStatement.getLabelEdges()) {\n          if (edge.labeled && edge.explicit) {\n            Statement source = edge.getSource();\n            if (myRootSwitchStatement.containsStatement(source)) {\n              continue;\n            }\n            Statement parent = myRootSwitchStatement.getParent();\n            if (!(parent instanceof SequenceStatement sequenceStatement && sequenceStatement.getStats().size() == 2)) {\n              return null;\n            }\n            int index = sequenceStatement.getStats().indexOf(myRootSwitchStatement);\n            if (index != 0) {\n              return null;\n            }\n            if (!(sequenceStatement.getStats().get(1) instanceof DoStatement firstDo &&\n                  firstDo.getStats().size() == 1 && firstDo.getStats().get(0) instanceof DoStatement nestedDo)) {\n              return null;\n            }\n            if (nestedDo.containsStatement(source)) {\n              continue;\n            }\n            return null;\n          }\n        }\n      }\n      return new Root(first, firstExprents, doParentStatement);\n    }\n\n    /**\n     * @return true if the bootstrap arguments of the switch selector can be processed (they represent Integer, String or Class),\n     * false otherwise.\n     */\n    private boolean checkBootstrap() {\n      List<PooledConstant> bootstrapArguments = mySwitchSelector.getBootstrapArguments();\n      if (bootstrapArguments == null) {\n        return true;\n      }\n      for (PooledConstant bootstrapArgument : bootstrapArguments) {\n        if (!(bootstrapArgument instanceof PrimitiveConstant primitiveConstant)) {\n          return true;\n        }\n        int type = primitiveConstant.type;\n        if (!(type == CodeConstants.CONSTANT_Integer || type == CodeConstants.CONSTANT_String || type == CodeConstants.CONSTANT_Class)) {\n          return true;\n        }\n      }\n      return false;\n    }\n\n    @Nullable\n    private static List<FullCase> resortForSwitchBootstrap(@NotNull SwitchStatement statement) {\n      for (Statement caseStatement : statement.getCaseStatements()) {\n        if (caseStatement == null) {\n          return null;\n        }\n      }\n      @NotNull List<List<@Nullable Exprent>> values = statement.getCaseValues();\n      List<List<Exprent>> sortedCaseValue = new ArrayList<>();\n      List<List<StatEdge>> sortedEdges = new ArrayList<>();\n      for (int i = 0; i < values.size(); i++) {\n        List<Exprent> caseValue = statement.getCaseValues().get(i);\n        for (Exprent exprent : caseValue) {\n          if (exprent == null) {\n            continue;\n          }\n          if (!(exprent instanceof ConstExprent constCaseValue)) {\n            return null;\n          }\n          if (!(constCaseValue.getConstType() == VARTYPE_INT ||\n                constCaseValue.getConstType() == VARTYPE_BYTECHAR ||\n                constCaseValue.getConstType() == VARTYPE_CHAR ||\n                constCaseValue.getConstType() == VARTYPE_BYTE)) {\n            return null;\n          }\n        }\n        List<StatEdge> edges = statement.getCaseEdges().get(i);\n        if (edges.size() != caseValue.size()) {\n          return null;\n        }\n        List<CaseValueWithEdge> sorted =\n          IntStream.range(0, edges.size()).mapToObj(ind -> new CaseValueWithEdge(caseValue.get(ind), edges.get(ind)))\n            .sorted(\n              Comparator.<CaseValueWithEdge, Boolean>comparing(o -> o.edge == statement.getDefaultEdge())\n                .thenComparingLong(o -> o.exprent instanceof ConstExprent c ? c.getIntValue() : Long.MIN_VALUE))\n            .toList();\n        sortedEdges.add(sorted.stream().map(t -> t.edge).collect(Collectors.toList()));\n        sortedCaseValue.add(sorted.stream().map(t -> t.exprent).collect(Collectors.toList()));\n      }\n\n      List<FullCase> sortedAll = IntStream.range(0, statement.getCaseValues().size())\n        .mapToObj(ind -> new FullCase(statement.getCaseStatements().get(ind),\n                                      sortedCaseValue.get(ind),\n                                      sortedEdges.get(ind)))\n        .sorted(Comparator.<FullCase, Boolean>comparing(\n            fullCase -> !fullCase.edges().isEmpty() && fullCase.edges().get(fullCase.exprents.size() - 1) == statement.getDefaultEdge())\n                  .thenComparingLong(o -> !o.exprents.isEmpty() && o.exprents.get(o.exprents.size() - 1) instanceof ConstExprent c\n                                          ? c.getIntValue()\n                                          : Long.MIN_VALUE))\n        .collect(Collectors.toList());\n\n      for (int i = 0; i < sortedAll.size(); i++) {\n        if (i == sortedAll.size() - 1) {\n          break;\n        }\n        FullCase current = sortedAll.get(i);\n        FullCase next = sortedAll.get(i + 1);\n        if (current.exprents.isEmpty() || next.exprents.isEmpty()) {\n          return null;\n        }\n\n        Exprent currentFirstExpr = current.exprents.get(0);\n        Exprent nextFirstExpr = next.exprents.get(0);\n        if (currentFirstExpr instanceof ConstExprent constExprent1 && nextFirstExpr instanceof ConstExprent constExprent2) {\n          if (constExprent1.getIntValue() > constExprent2.getIntValue() &&\n              constExprent1.getIntValue() != -1 &&\n              constExprent2.getIntValue() != -1) {\n            if (statement.getDefaultEdge() != next.edges.get(next.edges.size() - 1)) {\n              return null;\n            }\n          }\n        }\n        List<StatEdge> edges = current.statement.getSuccessorEdges(EdgeType.DIRECT_ALL);\n        if (edges.size() == 1) {\n          StatEdge currentEdge = edges.get(0);\n          if (currentEdge.getType() == EdgeType.REGULAR) {\n            Statement destination = currentEdge.getDestination();\n            if (destination != next.statement()) {\n              return null;\n            }\n          }\n        }\n      }\n      return sortedAll;\n    }\n\n    @NotNull\n    private static VarExprent createDefaultPatternVal(@NotNull String className) {\n      VarProcessor processor = DecompilerContext.getVarProcessor();\n      VarExprent varExprent = new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),\n                                             new VarType(CodeConstants.TYPE_OBJECT, 0, className),\n                                             processor);\n      varExprent.setDefinition(true);\n      processor.setVarName(varExprent.getVarVersionPair(), VarExprent.getName(varExprent.getVarVersionPair()));\n      return varExprent;\n    }\n\n    /**\n     * @return A map of case statements from switch and their pattern variable candidates, or null if the collection fails.\n     */\n    @Nullable\n    private Map<Statement, List<PatternVariableCandidate>> collectPatterns(@NotNull VarExprent instance,\n                                                                           @Nullable DoStatement doStatement,\n                                                                           @NotNull Set<AssignmentExprent> assignmentExprents) {\n\n      Map<Statement, List<PatternVariableCandidate>> candidates = new HashMap<>();\n      List<PooledConstant> bootstrapArguments = mySwitchSelector.getBootstrapArguments();\n      Map<Integer, String> mapCaseClasses = getMapCaseClasses(bootstrapArguments);\n      @NotNull List<List<@Nullable Exprent>> values = myRootSwitchStatement.getCaseValues();\n      for (int caseIndex = 0; caseIndex < values.size(); caseIndex++) {\n        List<Exprent> caseValues = values.get(caseIndex);\n        for (int valueIndex = 0; valueIndex < caseValues.size(); valueIndex++) {\n          Exprent caseValue = caseValues.get(valueIndex);\n          if (!(caseValue instanceof ConstExprent constCaseValue)) {\n            continue;\n          }\n          int expectedValue = constCaseValue.getIntValue();\n          String className = mapCaseClasses.get(expectedValue);\n          //if it doesn't have className, it is not a pattern (enum or literal), and can be processed during transformation\n          if (className == null) {\n            continue;\n          }\n\n          Statement caseStatement = myRootSwitchStatement.getCaseStatements().get(caseIndex);\n\n          VarTracker currentVarTracker = myVarTracker;\n          if (currentVarTracker == null) {\n            RecordVarExprent recordVarExprent = new RecordVarExprent(instance);\n            currentVarTracker = new VarTracker(recordVarExprent);\n            currentVarTracker.put(instance, recordVarExprent, myRootSwitchStatement);\n          }\n\n          ConstExprent checkType = new ConstExprent(new VarType(className), null, null);\n          currentVarTracker = currentVarTracker.copy();\n          if (currentVarTracker == null) {\n            continue;\n          }\n\n          PatternVariableCandidate candidate = null;\n\n          //example:\n          // some blocks\n          // var2 = 2 // here var2 is second initializer\n          // break\n          //'break' breaks graph for inner statements, that's why it is needed to be excluded temporary\n          if (caseStatement instanceof SequenceStatement sequenceStatement && !sequenceStatement.getStats().isEmpty()) {\n            VBStyleCollection<Statement, Integer> sequenceStatementStats = sequenceStatement.getStats();\n            Statement last = sequenceStatementStats.get(sequenceStatementStats.size() - 1);\n            if (last instanceof BasicBlockStatement && last.getExprents() != null && last.getExprents().size() == 1) {\n              Exprent exprent = last.getExprents().get(0);\n              if (exprent instanceof AssignmentExprent assignmentExprent &&\n                  assignmentExprent.getLeft() instanceof VarExprent left && myTypeVars.contains(left)) {\n                //new sequence without break\n                SequenceStatement newSequence = new SequenceStatement(sequenceStatementStats.subList(0, sequenceStatementStats.size() - 1));\n                Map<Statement, Statement> previousParents = new HashMap<>();\n                for (Statement currentStat : newSequence.getStats()) {\n                  previousParents.put(currentStat, currentStat.getParent());\n                  currentStat.setParent(newSequence);\n                }\n                newSequence.setParent(sequenceStatement.getParent());\n                candidate = findNextPatternVarCandidate(newSequence, instance, checkType, currentVarTracker, newSequence);\n                //'last' is not added here, because it must be processed in guards\n\n                //return 'break' back\n                for (Statement currentStat : newSequence.getStats()) {\n                  Statement currentParent = previousParents.get(currentStat);\n                  if (currentParent != null) {\n                    currentStat.setParent(currentParent);\n                  }\n                }\n\n                //candidate.nextStatement has broken edge here, it refers to 'last' (break)\n                //it is expected if it has a guard\n                if (candidate != null) {\n                  candidate =\n                    normalizeCandidateWithBrokenEdges(candidate, newSequence, sequenceStatement, last, doStatement, assignmentExprents,\n                                                      assignmentExprent);\n                  //it is impossible to normalize\n                  if (candidate == null) {\n                    return null;\n                  }\n                }\n              }\n            }\n          }\n\n          if (candidate == null) {\n            candidate = findNextPatternVarCandidate(caseStatement, instance, checkType, currentVarTracker, caseStatement);\n          }\n\n          if (candidate != null) {\n            List<PatternVariableCandidate> nestedSwitches =\n              tryToFindNestedSwitch(candidate, caseStatement, assignmentExprents, currentVarTracker);\n            if (nestedSwitches == null || nestedSwitches.isEmpty()) {\n              //something broken\n              return null;\n            }\n            candidates.put(caseStatement, nestedSwitches);\n          }\n          //there are no patterns for JAVA_LANG_OBJECT, add them as is\n          else if (JAVA_LANG_OBJECT.equals(className)) {\n            List<PatternVariableCandidate> value = new ArrayList<>();\n            value.add(\n              new PatternVariableCandidate(createDefaultPatternVal(className), caseStatement, new HashSet<>(), new ArrayList<>(), () -> {\n              }));\n            candidates.put(caseStatement, value);\n          }\n        }\n        //collect from default case\n        if (myRootSwitchStatement.getCaseEdges().get(caseIndex).contains(myRootSwitchStatement.getDefaultEdge())) {\n          Statement defaultStatement = myRootSwitchStatement.getCaseStatements().get(caseIndex);\n          if (defaultStatement instanceof BasicBlockStatement && defaultStatement.getExprents() != null &&\n              defaultStatement.getExprents().size() == 1 &&\n              defaultStatement.getExprents().get(0) instanceof AssignmentExprent assignmentExprent &&\n              myTypeVars.contains(assignmentExprent.getLeft())) {\n            assignmentExprents.add(assignmentExprent);\n          }\n        }\n      }\n      return candidates;\n    }\n\n    /**\n     * Collects the guards and returns a PatternContainer object.\n     *\n     * @param candidates        a map containing the candidates for each case statement\n     * @param typeVar           the type variable\n     * @param doParentStatement the parent statement of the do statement\n     * @param usedAssignments   a set of used assignments\n     * @return a PatternContainer object containing the collected guards\n     */\n    @NotNull\n    private SwitchPatternHelper.PatternContainer collectGuards(@NotNull Map<Statement, List<PatternVariableCandidate>> candidates,\n                                                               @NotNull Exprent typeVar,\n                                                               @Nullable Statement doParentStatement,\n                                                               @NotNull Set<AssignmentExprent> usedAssignments) {\n      PatternContainer container = new PatternContainer();\n      @NotNull List<Statement> statements = myRootSwitchStatement.getCaseStatements();\n      for (int i = 0; i < statements.size(); i++) {\n        Statement previousValueStatement = statements.get(i);\n        Statement caseStatement = statements.get(i);\n        List<PatternVariableCandidate> variableCandidates = candidates.get(caseStatement);\n        if (variableCandidates != null) {\n          for (PatternVariableCandidate variableCandidate : variableCandidates) {\n            container.addPattern(previousValueStatement, variableCandidate.getGuards(), variableCandidate.getNextStatement(),\n                                 variableCandidate.getVarExprent());\n          }\n          //java 21, only one pattern can be with guard\n          //todo check with unnamed patterns and variables\n          if (variableCandidates.size() != 1) {\n            continue;\n          }\n          caseStatement = variableCandidates.get(0).getNextStatement();\n        }\n        if (caseStatement.getStats().size() < 2) {\n          continue;\n        }\n        if (doParentStatement == null) {\n          continue;\n        }\n        List<@Nullable Exprent> exprents = myRootSwitchStatement.getCaseValues().get(i);\n        OptionalInt maxCaseValue = exprents.stream().filter(t -> t instanceof ConstExprent)\n          .mapToInt(t -> ((ConstExprent)t).getIntValue())\n          .max();\n        if (maxCaseValue.isEmpty()) {\n          continue;\n        }\n        boolean guardIsNegated = false;\n\n        if (!(caseStatement.getStats().get(0) instanceof IfStatement ifStatement &&\n              ifStatement.getIfstat() != null &&\n              ifStatement.getElsestat() == null &&\n              ifStatement.iftype == IFTYPE_IF)) {\n          continue;\n        }\n        AssignmentExprent assignmentExprent = null;\n\n        if (ifStatement.getIfstat().getExprents() != null &&\n            ifStatement.getIfstat().getExprents().size() == 1 &&\n            ifStatement.getIfstat().getExprents().get(0) instanceof AssignmentExprent expectedAssignmentExprent &&\n            expectedAssignmentExprent.getLeft() != typeVar && expectedAssignmentExprent.getRight() instanceof ConstExprent constExprent &&\n            constExprent.getValue() instanceof Integer index && index > maxCaseValue.getAsInt()) {\n          assignmentExprent = expectedAssignmentExprent;\n          guardIsNegated = true;\n        }\n\n        if (!guardIsNegated) {\n          Statement breakStatement = caseStatement.getStats().get(1);\n          Statement expectedBreakStatement = caseStatement.getStats().get(caseStatement.getStats().size() - 1);\n          List<StatEdge> successorEdges = expectedBreakStatement.getSuccessorEdges(EdgeType.DIRECT_ALL);\n          if (successorEdges.size() != 1) {\n            continue;\n          }\n          StatEdge breakEdge = successorEdges.get(0);\n          if (breakEdge.getType() == EdgeType.REGULAR) {\n            continue;\n          }\n          if (breakEdge.getDestination() != doParentStatement) {\n            continue;\n          }\n          if (!(breakStatement.getExprents() != null &&\n                breakStatement.getExprents().size() == 1 &&\n                breakStatement.getExprents().get(0) instanceof AssignmentExprent expectedAssignmentExprent &&\n                expectedAssignmentExprent.getLeft() != typeVar &&\n                expectedAssignmentExprent.getRight() instanceof ConstExprent constExprent &&\n                constExprent.getValue() instanceof Integer index &&\n                index > maxCaseValue.getAsInt())) {\n            continue;\n          }\n          assignmentExprent = expectedAssignmentExprent;\n          Statement ifstat = ifStatement.getIfstat();\n          List<StatEdge> edges = ifstat.getSuccessorEdges(EdgeType.DIRECT_ALL);\n          if (!edges.isEmpty() && (edges.size() != 1 || edges.get(0).getType() == EdgeType.REGULAR)) {\n            continue;\n          }\n        }\n\n        if (!(ifStatement.getHeadexprentList().size() == 1 &&\n              ifStatement.getHeadexprent() != null &&\n              ifStatement.getHeadexprent().getCondition() != null &&\n              ifStatement.getIfstat() != null)) {\n          continue;\n        }\n\n        IfExprent ifExprent = ifStatement.getHeadexprent();\n        Statement newCaseStatement = ifStatement.getIfstat();\n        if (guardIsNegated) {\n          ifExprent = ifExprent.negateIf();\n          newCaseStatement = new SequenceStatement(caseStatement.getStats().subList(1, caseStatement.getStats().size()));\n        }\n        Exprent nameAssignment = Optional.ofNullable(ifStatement.getStats())\n          .map(stats -> !stats.isEmpty() ? stats.get(0).getExprents() : null)\n          .map(exprs -> exprs.size() == 1 ? exprs.get(0) : null)\n          .orElse(null);\n\n        usedAssignments.add(assignmentExprent);\n        VarExprent nextValue = null;\n        if (nameAssignment instanceof AssignmentExprent nameAssignmentExprent &&\n            nameAssignmentExprent.getLeft() instanceof VarExprent varExprent) {\n          myTempVarAssignments.add(new TempVarAssignmentItem(varExprent, ifStatement));\n          nextValue = varExprent;\n        }\n\n        if (variableCandidates != null && variableCandidates.size() == 1) {\n          nextValue = variableCandidates.get(0).getVarExprent();\n        }\n        container.replacePattern(previousValueStatement, ifExprent.getCondition(), newCaseStatement, nextValue);\n      }\n      return container;\n    }\n\n    /**\n     * Normalizes the given PatternVariableCandidate by checking for broken edges and making the necessary adjustments.\n     *\n     * @param oldCandidate              The original PatternVariableCandidate to normalize.\n     * @param newSequence               The new SequenceStatement being added.\n     * @param previousSequenceStatement The previous SequenceStatement.\n     * @param last                      The Statement that contains the break.\n     * @param doStatement               The Root DoStatement\n     * @param assignmentExprents        The set of AssignmentExprents to collect.\n     * @param assignmentExprent         The AssignmentExprent to add (from last).\n     * @return The normalized PatternVariableCandidate or null if normalization fails.\n     */\n    @Nullable\n    private PatternVariableCandidate normalizeCandidateWithBrokenEdges(@NotNull PatternVariableCandidate oldCandidate,\n                                                                       @NotNull SequenceStatement newSequence,\n                                                                       @NotNull SequenceStatement previousSequenceStatement,\n                                                                       @NotNull Statement last,\n                                                                       @Nullable DoStatement doStatement,\n                                                                       @NotNull Set<AssignmentExprent> assignmentExprents,\n                                                                       @NotNull AssignmentExprent assignmentExprent) {\n\n      PatternVariableCandidate candidate = oldCandidate;\n      Statement nextStatement = candidate.getNextStatement();\n      if (nextStatement == newSequence) {\n        //if it has the same thing, let's reuse what we have; otherwise it will lead to a broken graph\n        candidate = new PatternVariableCandidate(candidate.getVarExprent(), previousSequenceStatement,\n                                                 candidate.getUsedIfStatement(), candidate.getTempAssignments(),\n                                                 candidate.getCleaner()\n        );\n      }\n      else if (nextStatement instanceof IfStatement ifStatement &&\n               ifStatement.getSuccessorEdges(EdgeType.DIRECT_ALL).size() == 1 &&\n               ifStatement.getSuccessorEdges(EdgeType.DIRECT_ALL).get(0).getDestination() == last) {\n        ArrayList<Statement> statements = new ArrayList<>();\n        statements.add(ifStatement);\n        statements.add(last);\n        candidate = new PatternVariableCandidate(candidate.getVarExprent(), new SequenceStatement(statements),\n                                                 candidate.getUsedIfStatement(), candidate.getTempAssignments(),\n                                                 candidate.getCleaner());\n      }\n      else if (nextStatement instanceof SequenceStatement nextSeqStat) {\n        if (nextSeqStat.getStats().isEmpty()) {\n          return null;\n        }\n        Statement lastNextStatement = nextSeqStat.getStats().get(nextSeqStat.getStats().size() - 1);\n        List<StatEdge> edges = lastNextStatement.getSuccessorEdges(EdgeType.DIRECT_ALL);\n        if (edges.size() != 1) {\n          return null;\n        }\n        StatEdge edge = edges.get(0);\n        if (edge.getDestination() != last) {\n          //edge contains break, so it can be assumed that the firstStatement of assignmentExprent is processed\n          if (!edgeCanBeWayOutOfRoot(doStatement, edge)) {\n            //something broken\n            return null;\n          }\n          assignmentExprents.add(assignmentExprent);\n        }\n        nextSeqStat.getStats().addWithKey(last, last.id);\n      }\n      else {\n        List<StatEdge> edges = nextStatement.getSuccessorEdges(EdgeType.ALL);\n        if (edges.size() == 1 && edges.get(0).getType() == EdgeType.BREAK) {\n          StatEdge edge = edges.get(0);\n          //edge contains break, so it can be assumed that the firstStatement of assignmentExprent is processed\n          if (edgeCanBeWayOutOfRoot(doStatement, edge)) {\n            assignmentExprents.add(assignmentExprent);\n          }\n        }\n      }\n      return candidate;\n    }\n\n    private boolean edgeCanBeWayOutOfRoot(@Nullable DoStatement doStatement, @NotNull StatEdge edge) {\n      return edge.getDestination() instanceof DummyExitStatement ||\n             (doStatement == null && edge.closure == myRootSwitchStatement) ||\n             (doStatement != null && edge.closure == doStatement) ||\n             (doStatement != null &&\n              edge.closure instanceof DoStatement upperDoStatement &&\n              upperDoStatement.containsStatement(doStatement)) ||\n             outsideCatch(edge, doStatement);\n    }\n\n    private boolean outsideCatch(@NotNull StatEdge edge, @Nullable DoStatement doStatement) {\n      Statement destination = edge.getDestination();\n      Statement parent = destination.getParent();\n      int indexOf = parent.getStats().indexOf(destination);\n      if (indexOf < 1) {\n        return false;\n      }\n      Statement previousStatement = parent.getStats().get(indexOf - 1);\n      return previousStatement.containsStatement(myRootSwitchStatement) &&\n             (doStatement == null || previousStatement.containsStatement(doStatement));\n    }\n\n    /**\n     * Tries to find a nested switch statement according to patterns and process them recursively.\n     *\n     * @param previousCandidate      The previous pattern variable candidate\n     * @param caseStatement          The case statement (from a previous switch statement)\n     * @param usedAssignmentExprents The used assignment expressions\n     * @param varTracker             The variable tracker\n     * @return A list of pattern variable candidates or null, if it is impossible\n     * List with only previousCandidate can be returned, if nested switch is not found\n     */\n    @Nullable\n    private List<PatternVariableCandidate> tryToFindNestedSwitch(@NotNull PatternVariableCandidate previousCandidate,\n                                                                 @NotNull Statement caseStatement,\n                                                                 @NotNull Set<AssignmentExprent> usedAssignmentExprents,\n                                                                 @NotNull VarTracker varTracker) {\n      Set<AssignmentExprent> toAddAssignmentExprent = new HashSet<>();\n      List<PatternVariableCandidate> defaultValue = new ArrayList<>();\n      defaultValue.add(previousCandidate);\n\n      Statement currentStatement = previousCandidate.getNextStatement();\n      VBStyleCollection<Statement, Integer> currentStats = currentStatement.getStats();\n      if (currentStats.isEmpty()) {\n        return defaultValue;\n      }\n      SwitchStatement nestedSwitchStatement = null;\n      if (!caseStatement.getLabelEdges().isEmpty()) {\n        //example:\n        //Sequence\n        //  Switch\n        //    case default\n        //    case pattern\n        //      initializers\n        //  DoStatement\n        //    DoStatement\n        //      nested Switch\n        Statement nextStatement = previousCandidate.getNextStatement();\n        if (!(nextStatement instanceof SequenceStatement sequenceStatement && sequenceStatement.getStats().size() == 1 &&\n              sequenceStatement.getStats().get(0) instanceof BasicBlockStatement && caseStatement.getLabelEdges().size() == 1)) {\n          return defaultValue;\n        }\n        StatEdge edge = caseStatement.getLabelEdges().iterator().next();\n        if (!(edge.getType() == EdgeType.BREAK && edge.getDestination() instanceof DoStatement upperDoStatement &&\n              upperDoStatement.getConditionExprent() == null && upperDoStatement.getStats().size() == 1 &&\n              upperDoStatement.getStats().get(0) instanceof DoStatement nestedDoStatement &&\n              nestedDoStatement.getConditionExprent() == null && nestedDoStatement.getStats().size() == 1 &&\n              nestedDoStatement.getStats().get(0) instanceof SwitchStatement switchStatement)) {\n          return defaultValue;\n        }\n        if (!(caseStatement.getParent() instanceof SwitchStatement upperSwitchStatement &&\n              upperSwitchStatement.getCaseStatements().size() == 2)) {\n          return defaultValue;\n        }\n        int indexCurrentStat = upperSwitchStatement.getCaseStatements().indexOf(caseStatement);\n        if (indexCurrentStat == -1) {\n          return defaultValue;\n        }\n        int indexDefaultStatement = indexCurrentStat == 0 ? 1 : 0;\n        if (!upperSwitchStatement.getCaseEdges()\n          .get(indexDefaultStatement)\n          .contains(upperSwitchStatement.getDefaultEdge())) {\n          return defaultValue;\n        }\n        Statement defaultStatement = upperSwitchStatement.getCaseStatements().get(indexDefaultStatement);\n        if (defaultStatement.getExprents() == null) {\n          return defaultValue;\n        }\n        for (Exprent defaultExprent : defaultStatement.getExprents()) {\n          if (defaultExprent instanceof AssignmentExprent assignmentExprent) {\n            toAddAssignmentExprent.add(assignmentExprent);\n          }\n        }\n        nestedSwitchStatement = switchStatement;\n      }\n      //current is switch\n      else if (currentStats.get(0) instanceof SwitchStatement switchStatement) {\n        nestedSwitchStatement = switchStatement;\n      }\n      else {\n        if (currentStats.size() < 2) {\n          return defaultValue;\n        }\n        if (!(currentStats.get(0) instanceof BasicBlockStatement)) {\n          return defaultValue;\n        }\n        //Example:\n        //Sequence\n        //  BasicBlock\n        //  Switch\n        if (currentStats.get(1) instanceof SwitchStatement switchStatement) {\n          nestedSwitchStatement = switchStatement;\n        }\n        //Example:\n        //Sequence\n        //  BasicBlock\n        //  Do\n        //    Switch\n        if (currentStats.get(1) instanceof DoStatement doStatement && !doStatement.getStats().isEmpty() &&\n            doStatement.getStats().get(0) instanceof SwitchStatement switchStatement) {\n          nestedSwitchStatement = switchStatement;\n        }\n        //Example:\n        //Sequence\n        //  BasicBlock\n        //  Do\n        //    Sequence\n        //      Switch\n        if (currentStats.get(1) instanceof DoStatement doStatement && doStatement.getStats().size() == 1 &&\n            doStatement.getStats().get(0) instanceof SequenceStatement sequenceStatement &&\n            sequenceStatement.getStats().size() == 2 &&\n            sequenceStatement.getStats().get(0) instanceof SwitchStatement newSwitchStatement) {\n          nestedSwitchStatement = newSwitchStatement;\n        }\n        if (currentStats.get(1) instanceof DoStatement doStatement && doStatement.getStats().size() == 1 &&\n            doStatement.getStats().get(0) instanceof DoStatement nestedDoStatement &&\n            nestedDoStatement.getStats().size() == 1 &&\n            nestedDoStatement.getStats().get(0) instanceof SwitchStatement newSwitchStatement) {\n          nestedSwitchStatement = newSwitchStatement;\n        }\n      }\n\n      if (nestedSwitchStatement == null) {\n        return defaultValue;\n      }\n      if (!(nestedSwitchStatement.getHeadExprent() instanceof SwitchExprent switchExprent &&\n            switchExprent.getValue() instanceof InvocationExprent invocationExprent)) {\n        return defaultValue;\n      }\n\n      SwitchOnReferenceCandidate recognized =\n        new JavacReferenceFinder(varTracker, myTypeVars, nestedSwitchStatement, invocationExprent).findCandidate();\n      if (recognized == null) {\n        return defaultValue;\n      }\n      VarExprent exprent = previousCandidate.getVarExprent();\n      if (!(exprent instanceof RecordVarExprent currentRecordDeconstruction)) {\n        return defaultValue;\n      }\n      RecordVarExprent component = currentRecordDeconstruction.getDirectComponent(recognized.myPreviousSelector.getInstance());\n      if (component == null) {\n        return defaultValue;\n      }\n      ArrayList<PatternVariableCandidate> candidates = new ArrayList<>();\n      Map<Integer, String> classes = getMapCaseClasses(recognized.myPreviousSelector.getBootstrapArguments());\n      for (FullCase fullCase : recognized.mySortedCasesFromRoot) {\n        List<Exprent> fullCaseExprents = fullCase.exprents;\n        Statement nestedStatement = fullCase.statement;\n        List<StatEdge> edges = nestedStatement.getSuccessorEdges(EdgeType.DIRECT_ALL);\n        if (edges.size() == 1) {\n          StatEdge edge = edges.get(0);\n          //not accurate, but it is hard to track all labels here\n          if ((edge.getType() == EdgeType.CONTINUE || edge.getType() == EdgeType.BREAK) &&\n              nestedStatement.getExprents() != null) {\n            for (Exprent nestedExprent : nestedStatement.getExprents()) {\n              if (nestedExprent instanceof AssignmentExprent assignmentExprent &&\n                  assignmentExprent.getLeft() instanceof VarExprent left &&\n                  myTypeVars.contains(left)) {\n                toAddAssignmentExprent.add(assignmentExprent);\n              }\n            }\n          }\n        }\n        List<PatternContainer.PatternStatement> nestedPatterns = recognized.myPatternContainer.getPatterns(nestedStatement);\n        //process literals\n        if (nestedPatterns == null || nestedPatterns.isEmpty()) {\n          if (fullCaseExprents.size() == 2) {\n            //delete null\n            if (fullCaseExprents.get(0) instanceof ConstExprent constExprent && constExprent.getIntValue() == -1) {\n              fullCaseExprents.remove(fullCaseExprents.get(0));\n            }\n          }\n          if (fullCaseExprents.size() != 1) {\n            return null;\n          }\n          if (fullCaseExprents.get(0) == null) {\n            continue;\n          }\n          if (!(fullCaseExprents.get(0) instanceof ConstExprent constExprent)) {\n            return null;\n          }\n          String newClassName = classes.get(constExprent.getIntValue());\n          if (newClassName == null) {\n            return null;\n          }\n          RecordVarExprent copy = currentRecordDeconstruction.copy();\n          RecordVarExprent toReplace = copy.getDirectComponent(recognized.myPreviousSelector.getInstance());\n          if (toReplace == null) {\n            return null;\n          }\n          toReplace.setVarType(new VarType(newClassName));\n          PatternVariableCandidate nestedPatternVariableCandidate =\n            new PatternVariableCandidate(copy, fullCase.statement(), previousCandidate.getUsedIfStatement(),\n                                         previousCandidate.getTempVarAssignments(), previousCandidate.getCleaner());\n          candidates.add(nestedPatternVariableCandidate);\n        }\n        else {\n          //process patterns\n          for (PatternContainer.PatternStatement nestedPattern : nestedPatterns) {\n            RecordVarExprent copy;\n            if (nestedPattern.variable instanceof RecordVarExprent recordVarExprent) {\n              copy = recordVarExprent;\n            }\n            else {\n              if (nestedPattern.variable == null) {\n                return null;\n              }\n              copy = currentRecordDeconstruction.copy();\n              RecordVarExprent toReplace = copy.getDirectComponent(recognized.myPreviousSelector.getInstance());\n              if (toReplace == null) {\n                return null;\n              }\n              if (!toReplace.copyFrom(nestedPattern.variable)) {\n                return null;\n              }\n            }\n            PatternVariableCandidate nestedPatternVariableCandidate =\n              new PatternVariableCandidate(copy, nestedPattern.caseStatement, previousCandidate.getUsedIfStatement(),\n                                           previousCandidate.getTempVarAssignments(), previousCandidate.getCleaner());\n            nestedPatternVariableCandidate.setGuards(nestedPattern.guard);\n            candidates.add(nestedPatternVariableCandidate);\n          }\n        }\n      }\n\n      if (!candidates.isEmpty()) {\n        candidates.get(0).getTempAssignments().addAll(recognized.myTempVarAssignments);\n      }\n      usedAssignmentExprents.addAll(recognized.myUsedTypeVarAssignments);\n      usedAssignmentExprents.addAll(toAddAssignmentExprent);\n      myUsedSwitch.add(nestedSwitchStatement);\n      return candidates;\n    }\n  }\n\n  @NotNull\n  private static Map<Integer, String> getMapCaseClasses(List<PooledConstant> bootstrapArguments) {\n    Map<Integer, String> mapCaseClasses = new HashMap<>();\n    for (int i = 0; i < bootstrapArguments.size(); i++) {\n      PooledConstant constant = bootstrapArguments.get(i);\n      if (constant instanceof PrimitiveConstant primitiveConstant) {\n        if (primitiveConstant.type == CodeConstants.CONSTANT_Class) {\n          mapCaseClasses.put(i, primitiveConstant.getString());\n        }\n      }\n    }\n    return mapCaseClasses;\n  }\n\n  private static boolean isNonNullCheck(@NotNull Exprent exprent, @NotNull Exprent switchVar) {\n    if (!(exprent instanceof InvocationExprent invocationExprent)) {\n      return false;\n    }\n    if (!invocationExprent.isStatic() || !\"requireNonNull\".equals(invocationExprent.getName()) ||\n        !JAVA_UTIL_OBJECTS.equals(invocationExprent.getClassName()) ||\n        invocationExprent.getParameters() == null ||\n        invocationExprent.getParameters().size() != 1 ||\n        !switchVar.equals(invocationExprent.getParameters().get(0))) {\n      return false;\n    }\n    return true;\n  }\n\n  /**\n   * Check if a variable is reinitialized a new value within a given statement and its child statements.\n   * It checks if all nested statements are processed correctly for a given switch statement\n   *\n   * @param var       The variable expression to be reinitialized.\n   * @param statement The statement in which to search for reinitialization of the variable.\n   * @param exclude   A set of assignment expressions to be excluded from reinitialization check.\n   * @return true if the variable is reinitialized in the given statement or its child statements, false otherwise.\n   */\n  private static boolean checkReinitVar(@NotNull Exprent var, @NotNull Statement statement, @NotNull Set<AssignmentExprent> exclude) {\n    List<Exprent> exprents = statement.getExprents();\n    if (exprents != null) {\n      for (Exprent exprent : exprents) {\n        if (!(exprent instanceof AssignmentExprent assignmentExprent)) {\n          continue;\n        }\n        if (exclude.contains(assignmentExprent)) {\n          continue;\n        }\n        Exprent left = assignmentExprent.getLeft();\n        if (var.equals(left)) {\n          return true;\n        }\n      }\n    }\n    if (statement.getStats() != null) {\n      for (Statement children : statement.getStats()) {\n        if (checkReinitVar(var, children, exclude)) {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n\n  /**\n   * This class represents a reference candidate for switch with reference expression.\n   * It can convert a usual switch statement to switch a statement with patterns.\n   */\n  private static class SwitchOnReferenceCandidate implements SwitchOnCandidate {\n    @NotNull\n    private final SwitchStatement myRootSwitchStatement;\n    @NotNull\n    private final InvocationExprent myPreviousSelector;\n    @NotNull\n    private final Exprent myNewSwitchSelectorVariant;\n    @NotNull\n    private final List<TempVarAssignmentItem> myTempVarAssignments;\n    @NotNull\n    private final List<JavacReferenceFinder.FullCase> mySortedCasesFromRoot;\n\n    @NotNull\n    private final Set<SwitchStatement> myUsedSwitchStatements;\n    @NotNull\n    private final Set<AssignmentExprent> myUsedTypeVarAssignments;\n    @Nullable\n    private final Runnable myCleaner;\n    @NotNull\n    private final SwitchPatternHelper.PatternContainer myPatternContainer;\n    @Nullable\n    private final DoStatement myUppedDoStatement;\n    private final boolean myMustCleanNonNull;\n\n    private SwitchOnReferenceCandidate(@NotNull SwitchStatement rootSwitchStatement,\n                                       @NotNull InvocationExprent previousSelector,\n                                       @NotNull Exprent newSwitchSelectorVariant,\n                                       @NotNull List<JavacReferenceFinder.FullCase> cases,\n                                       @NotNull SwitchPatternHelper.PatternContainer patternContainer,\n                                       @NotNull List<TempVarAssignmentItem> tempVarAssignments,\n                                       @NotNull Set<SwitchStatement> usedSwitchStatements,\n                                       @NotNull Set<AssignmentExprent> usedTypeVarAssignments,\n                                       @Nullable DoStatement uppedDoStatement,\n                                       boolean mustCleanNonNull,\n                                       @Nullable Runnable cleaner) {\n      this.myRootSwitchStatement = rootSwitchStatement;\n      this.myPreviousSelector = previousSelector;\n      this.myNewSwitchSelectorVariant = newSwitchSelectorVariant;\n      this.myTempVarAssignments = tempVarAssignments;\n      this.mySortedCasesFromRoot = cases;\n      this.myUsedSwitchStatements = usedSwitchStatements;\n      this.myCleaner = cleaner;\n      this.myPatternContainer = patternContainer;\n      this.myMustCleanNonNull = mustCleanNonNull;\n      this.myUppedDoStatement = uppedDoStatement;\n      this.myUsedTypeVarAssignments = usedTypeVarAssignments;\n    }\n\n    @Override\n    public void simplify() {\n      prepareSortedCases();\n\n      if (myCleaner != null) {\n        myCleaner.run();\n      }\n      resort(myRootSwitchStatement, mySortedCasesFromRoot);\n      Exprent headExprent = myRootSwitchStatement.getHeadExprent();\n      List<PooledConstant> bootstrapArguments = myPreviousSelector.getBootstrapArguments();\n      Map<Integer, String> mapCaseClasses = getMapCaseClasses(bootstrapArguments);\n      Map<Integer, Exprent> mapCaseValue = getMapCaseValue(bootstrapArguments);\n\n      boolean hasPattern = remapCaseValues(mapCaseValue, mapCaseClasses);\n\n      if (headExprent != null) {\n        headExprent.replaceExprent(myPreviousSelector, myNewSwitchSelectorVariant);\n      }\n      if (hasPattern) {\n        remapWithPatterns(myRootSwitchStatement, myPatternContainer, myUppedDoStatement, myTempVarAssignments);\n        cleanDefault(myRootSwitchStatement);\n      }\n      Exprent oldSelector = myPreviousSelector.getInstance();\n      if (oldSelector instanceof VarExprent oldSelectorVarExprent &&\n          myNewSwitchSelectorVariant instanceof VarExprent newSwitchSelectorVarExprent) {\n        changeEverywhere(oldSelectorVarExprent, newSwitchSelectorVarExprent, myRootSwitchStatement.getCaseStatements());\n        changeDefaultToFullCase(myRootSwitchStatement, newSwitchSelectorVarExprent);\n      }\n    }\n\n    private static void changeDefaultToFullCase(@NotNull SwitchStatement myRoot, @NotNull VarExprent newSwitch) {\n      List<List<StatEdge>> edges = myRoot.getCaseEdges();\n      for (int i = 0; i < edges.size(); i++) {\n        List<StatEdge> statEdges = edges.get(i);\n        if (statEdges.size() == 1 && statEdges.get(0) == myRoot.getDefaultEdge()) {\n          Statement defaultStatement = myRoot.getCaseStatements().get(i);\n          Statement statementWithFirstAssignment = getStatementWithFirstAssignment(defaultStatement);\n          if (statementWithFirstAssignment != null &&\n              statementWithFirstAssignment.getExprents() != null &&\n              !statementWithFirstAssignment.getExprents().isEmpty() &&\n              statementWithFirstAssignment.getExprents().get(0) instanceof AssignmentExprent assignmentExprent &&\n              assignmentExprent.getRight() != null &&\n              assignmentExprent.getLeft() instanceof VarExprent newVarExprent &&\n              assignmentExprent.getLeft().getExprType() != null &&\n              assignmentExprent.getRight().equals(newSwitch) &&\n              assignmentExprent.getLeft().getExprType().equals(newSwitch.getExprType())) {\n            statementWithFirstAssignment.getExprents().remove(0);\n            List<@Nullable Exprent> defaultValues = myRoot.getCaseValues().get(i);\n            defaultValues.clear();\n            defaultValues.add(newVarExprent);\n            myRoot.setUseCustomDefault();\n            break;\n          }\n        }\n      }\n    }\n\n    @Nullable\n    private static Statement getStatementWithFirstAssignment(@NotNull Statement statement) {\n      if (statement.getExprents() != null && !statement.getExprents().isEmpty()) {\n        if (statement.getExprents().get(0) instanceof AssignmentExprent) {\n          return statement;\n        }\n        else {\n          return null;\n        }\n      }\n      if (!statement.getStats().isEmpty()) {\n        return getStatementWithFirstAssignment(statement.getStats().get(0));\n      }\n      return null;\n    }\n\n    private static void changeEverywhere(@NotNull VarExprent oldVariant,\n                                         @NotNull VarExprent newVariant,\n                                         @NotNull List<Statement> statements) {\n      for (Statement statement : statements) {\n        if (statement.getExprents() != null) {\n          for (Exprent exprent : statement.getExprents()) {\n            for (Exprent nestedExprent : exprent.getAllExprents()) {\n              if (nestedExprent.equals(oldVariant)) {\n                exprent.replaceExprent(nestedExprent, newVariant);\n              }\n            }\n          }\n        }\n        for (Statement nestedStat : statement.getStats()) {\n          changeEverywhere(oldVariant, newVariant, nestedStat.getStats());\n        }\n      }\n    }\n\n    @Override\n    public Set<SwitchStatement> usedSwitch() {\n      return myUsedSwitchStatements;\n    }\n\n    @Override\n    public List<TempVarAssignmentItem> prepareTempAssignments() {\n      return this.myTempVarAssignments;\n    }\n\n    @NotNull\n    private Map<Integer, Exprent> getMapCaseValue(List<PooledConstant> bootstrapArguments) {\n      Map<Integer, Exprent> mapCaseValue = new HashMap<>();\n      for (int i = 0; i < bootstrapArguments.size(); i++) {\n        PooledConstant constant = bootstrapArguments.get(i);\n        if (constant instanceof PrimitiveConstant primitiveConstant) {\n          if (primitiveConstant.type == CodeConstants.CONSTANT_Class) {\n            continue;\n          }\n          if (myPreviousSelector.isDynamicCall(\"enumSwitch\", 2)) {\n            mapCaseValue.put(i, new FieldExprent(primitiveConstant.getString(), null, true, null, null, null));\n          }\n          else {\n            VarType type = switch (primitiveConstant.type) {\n              case CodeConstants.CONSTANT_String -> VARTYPE_STRING;\n              case CodeConstants.CONSTANT_Integer -> VARTYPE_INT;\n              default -> VARTYPE_UNKNOWN;\n            };\n            mapCaseValue.put(i, new ConstExprent(type, primitiveConstant.value, null));\n          }\n        }\n      }\n      return mapCaseValue;\n    }\n\n    private void prepareSortedCases() {\n      for (int i = 0; i < mySortedCasesFromRoot.size(); i++) {\n        Statement currentStatement = mySortedCasesFromRoot.get(i).statement;\n        List<StatEdge> edges = currentStatement.getSuccessorEdges(EdgeType.DIRECT_ALL);\n        if (edges.size() == 1) {\n          StatEdge onlyEdge = edges.get(0);\n          if (onlyEdge.getType() != EdgeType.REGULAR && !onlyEdge.explicit) {\n            onlyEdge.explicit = true;\n            onlyEdge.labeled = false;\n          }\n          if (i == myRootSwitchStatement.getCaseStatements().size() - 1 &&\n              onlyEdge.getType() == EdgeType.BREAK &&\n              onlyEdge.explicit &&\n              !onlyEdge.labeled) {\n            onlyEdge.explicit = false;\n          }\n        }\n      }\n\n      if (myMustCleanNonNull) {\n        JavacReferenceFinder.FullCase toDelete = null;\n        for (JavacReferenceFinder.FullCase fullCase : mySortedCasesFromRoot) {\n          int nullIndex = -1;\n          List<Exprent> exprents = fullCase.exprents;\n          for (int i = 0; i < exprents.size(); i++) {\n            Exprent t = exprents.get(i);\n            if (t instanceof ConstExprent constExprent && constExprent.getIntValue() == -1) {\n              nullIndex = i;\n              break;\n            }\n          }\n          if (nullIndex == -1) {\n            continue;\n          }\n          if (fullCase.exprents().size() != 1) {\n            fullCase.exprents.remove(nullIndex);\n            fullCase.edges.remove(nullIndex);\n          }\n          else {\n            toDelete = fullCase;\n          }\n          break;\n        }\n        mySortedCasesFromRoot.remove(toDelete);\n      }\n    }\n\n    private boolean remapCaseValues(@NotNull Map<Integer, Exprent> mapCaseValue,\n                                    @NotNull Map<Integer, String> mapCaseClasses) {\n      @NotNull List<List<@Nullable Exprent>> values = myRootSwitchStatement.getCaseValues();\n      boolean hasPattern = false;\n      for (int caseIndex = 0; caseIndex < values.size(); caseIndex++) {\n        List<Exprent> caseValues = values.get(caseIndex);\n        for (int valueIndex = 0; valueIndex < caseValues.size(); valueIndex++) {\n          Exprent caseValue = caseValues.get(valueIndex);\n          if (!(caseValue instanceof ConstExprent constCaseValue)) {\n            continue;\n          }\n          int expectedValue = constCaseValue.getIntValue();\n          if (expectedValue == -1) {\n            caseValues.set(valueIndex, new ConstExprent(VARTYPE_NULL, null, null));\n          }\n          Exprent newCaseValue = mapCaseValue.get(expectedValue);\n          if (newCaseValue == null) {\n            String className = mapCaseClasses.get(expectedValue);\n            if (className == null) {\n              continue;\n            }\n            List<PatternContainer.PatternStatement> guards =\n              myPatternContainer.getPatterns(myRootSwitchStatement.getCaseStatements().get(caseIndex));\n            if (guards != null && guards.size() == 1) {\n              //if several, they will be remapped later\n              newCaseValue = guards.get(0).variable();\n            }\n            if (newCaseValue == null) {\n              newCaseValue = JavacReferenceFinder.createDefaultPatternVal(className);\n            }\n            hasPattern = true;\n          }\n          caseValues.set(valueIndex, newCaseValue);\n        }\n      }\n      return hasPattern;\n    }\n\n    private static void remapWithPatterns(@NotNull SwitchStatement switchStatement,\n                                          @Nullable SwitchPatternHelper.PatternContainer patternContainer,\n                                          @Nullable DoStatement upperDoStatement,\n                                          @NotNull List<TempVarAssignmentItem> tempVarAssignments) {\n      if (patternContainer == null) {\n        return;\n      }\n      extendCases(switchStatement, patternContainer);\n      addGuards(switchStatement, patternContainer);\n      if (upperDoStatement != null) {\n        upperDoStatement.getParent().replaceStatement(upperDoStatement, switchStatement);\n        normalizeCaseLabels(switchStatement, upperDoStatement);\n      }\n      normalizeLabels(switchStatement, tempVarAssignments);\n      deleteNullCases(switchStatement);\n    }\n\n    /**\n     * Deletes null cases from a SwitchStatement if these labels contain var exprent\n     * (it is impossible to have null and pattern named variable at the same time).\n     *\n     * @param statement the SwitchStatement from which to delete null cases\n     */\n    private static void deleteNullCases(@NotNull SwitchStatement statement) {\n      @NotNull List<List<@Nullable Exprent>> values = statement.getCaseValues();\n      for (int i = 0; i < values.size(); i++) {\n        List<Exprent> value = values.get(i);\n        int indexOfNull = -1;\n        Exprent nullExprent = null;\n        for (int j = 0; j < value.size(); j++) {\n          Exprent exprent = value.get(j);\n          if (exprent instanceof ConstExprent constExprent && constExprent.isNull()) {\n            nullExprent = constExprent;\n            indexOfNull = j;\n            break;\n          }\n        }\n        if (nullExprent != null && value.stream().anyMatch(t -> t instanceof VarExprent)) {\n          value.remove(nullExprent);\n          statement.getCaseEdges().get(i).remove(indexOfNull);\n        }\n      }\n    }\n\n    /**\n     * Adds guards to the cases of a given switch statement based on a pattern container.\n     * If a case has only one pattern, it replaces the statement with the corresponding pattern statement,\n     * sets the destination of the case edge to the pattern statement,\n     * and adds the guard to the switch statement.\n     *\n     * @param switchStatement  the switch statement to add guards to\n     * @param patternContainer the pattern container containing the patterns to match and add guards for\n     */\n    private static void addGuards(@NotNull SwitchStatement switchStatement, @NotNull PatternContainer patternContainer) {\n      for (int i = 0; i < switchStatement.getCaseStatements().size(); i++) {\n        Statement currentCaseStatement = switchStatement.getCaseStatements().get(i);\n        List<PatternContainer.PatternStatement> patterns = patternContainer.getPatterns(currentCaseStatement);\n        if (patterns != null && patterns.size() == 1) {\n          Statement newStatement = patterns.get(0).caseStatement();\n          switchStatement.replaceStatement(switchStatement.getCaseStatements().get(i), newStatement);\n          switchStatement.getCaseStatements().set(i, newStatement);\n          switchStatement.getCaseEdges().get(i).forEach(edge -> {\n                                                          edge.setDestination(patterns.get(0).caseStatement());\n                                                        }\n          );\n          Exprent guard = patterns.get(0).guard();\n          if (guard != null) {\n            switchStatement.addGuard(switchStatement.getCaseStatements().get(i), guard);\n          }\n        }\n      }\n    }\n\n    /**\n     * Extend or duplicate the cases of a given switch statement based on a pattern container.\n     *\n     * @param switchStatement the switch statement to extend the cases for\n     * @param patternContainer the pattern container containing the patterns to match and extend the cases\n     */\n    private static void extendCases(@NotNull SwitchStatement switchStatement, @NotNull PatternContainer patternContainer) {\n      for (Map.Entry<Statement, List<PatternContainer.PatternStatement>> entry : patternContainer.patternsByStatement.entrySet()) {\n        if (entry.getValue().size() == 1) {\n          continue;\n        }\n        Statement currentStatement = entry.getKey();\n        int indexOf = switchStatement.getCaseStatements().indexOf(currentStatement);\n        if (indexOf < 0) {\n          continue;\n        }\n\n        List<PatternContainer.PatternStatement> value = entry.getValue();\n        for (int i = value.size() - 1; i >= 0; i--) {\n          PatternContainer.PatternStatement patternStatement = value.get(i);\n          int duplicatedIndex = switchStatement.duplicateCaseStatement(currentStatement);\n          if (duplicatedIndex < 0) {\n            continue;\n          }\n          switchStatement.replaceStatement(switchStatement.getCaseStatements().get(duplicatedIndex), patternStatement.caseStatement);\n          switchStatement.getCaseEdges().get(duplicatedIndex).forEach(edge -> {\n                                                                        edge.setDestination(patternStatement.caseStatement);\n                                                                      }\n          );\n          VarExprent variable = patternStatement.variable();\n          ArrayList<Exprent> newList = new ArrayList<>();\n          newList.add(variable);\n          switchStatement.getCaseValues().set(duplicatedIndex, newList);\n          Exprent guard = patternStatement.guard;\n          if (guard != null) {\n            switchStatement.addGuard(patternStatement.caseStatement, guard);\n          }\n        }\n        switchStatement.removeCaseStatement(currentStatement);\n      }\n    }\n\n    /**\n     * Normalizes the labels in the given switch statement by removing labels that are not explicitly labeled\n     * and removing labels that correspond to assignments to temporary variables.\n     *\n     * @param switchStatement      the switch statement to normalize the labels for\n     * @param upperDoStatement     the upper DoStatement to consider during normalization\n     */\n    private static void normalizeCaseLabels(@NotNull SwitchStatement switchStatement, @NotNull DoStatement upperDoStatement) {\n      @NotNull List<Statement> statements = switchStatement.getCaseStatements();\n      for (int i = 0; i < statements.size(); i++) {\n        Statement caseStatement = statements.get(i);\n        List<StatEdge> edges = caseStatement.getSuccessorEdges(EdgeType.DIRECT_ALL);\n        if (edges.isEmpty() && !caseStatement.getStats().isEmpty()) {\n          edges = caseStatement.getStats().get(caseStatement.getStats().size() - 1).getSuccessorEdges(EdgeType.DIRECT_ALL);\n        }\n        if (edges.size() == 1 && edges.get(0).getType() == EdgeType.BREAK &&\n            (upperDoStatement.equals(edges.get(0).closure) || switchStatement.equals(edges.get(0).closure))) {\n          edges.get(0).labeled = false;\n        }\n      }\n    }\n\n    /**\n     * Normalizes the labels in the given switch statement by removing labels that are not explicitly labeled and\n     * removing labels that correspond to assignments to temporary variables.\n     *\n     * @param switchStatement        the switch statement to normalize the labels for\n     * @param tempVarAssignments     the list of temporary variable assignments used in the switch statement\n     */\n    private static void normalizeLabels(@NotNull SwitchStatement switchStatement,\n                                        @NotNull List<TempVarAssignmentItem> tempVarAssignments) {\n      Set<StatEdge> labelsToDelete = new HashSet<>();\n      Set<VarExprent> tempVars = tempVarAssignments.stream().map(t -> t.varExprent()).collect(Collectors.toSet());\n      for (StatEdge label : switchStatement.getLabelEdges()) {\n        if (!label.explicit || !label.labeled) {\n          continue;\n        }\n        Statement source = label.getSource();\n        if (!(source instanceof BasicBlockStatement basicBlockStatement)) {\n          continue;\n        }\n        boolean toDelete = true;\n        if (basicBlockStatement.getExprents() == null) {\n          continue;\n        }\n        for (Exprent exprent : basicBlockStatement.getExprents()) {\n          if (!(exprent instanceof AssignmentExprent assignmentExprent)) {\n            toDelete = false;\n            break;\n          }\n          if (!tempVars.contains(assignmentExprent.getLeft())) {\n            toDelete = false;\n            break;\n          }\n        }\n        if (toDelete) {\n          labelsToDelete.add(label);\n        }\n      }\n      switchStatement.getLabelEdges().removeAll(labelsToDelete);\n    }\n\n    /**\n     * Cleans up the default case of the given switch statement if it is possible,\n     * it is included clearing other case values, handling throwing an exception.\n     *\n     * @param statement switch statement to clean up\n     */\n    private static void cleanDefault(@NotNull SwitchStatement statement) {\n      @NotNull List<List<StatEdge>> caseEdges = statement.getCaseEdges();\n      int indexDefault = -1;\n      boolean deleteDefault = false;\n      for (int caseIndex = 0; caseIndex < caseEdges.size(); caseIndex++) {\n        List<StatEdge> edges = caseEdges.get(caseIndex);\n        int indexDefaultInside = edges.indexOf(statement.getDefaultEdge());\n        if (indexDefaultInside == -1) {\n          continue;\n        }\n        indexDefault = caseIndex;\n        //delete other case values for default\n        if (edges.size() != 1) {\n          List<Exprent> exprentsToRemove = new ArrayList<>();\n          List<StatEdge> edgesToRemove = new ArrayList<>();\n          List<@Nullable Exprent> exprents = statement.getCaseValues().get(caseIndex);\n          for (int i = 0; i < edges.size(); i++) {\n            if (edges.get(i) == statement.getDefaultEdge()) {\n              continue;\n            }\n            if (exprents.get(i) instanceof VarExprent) {\n              exprentsToRemove.add(exprents.get(i));\n              edgesToRemove.add(edges.get(i));\n            }\n          }\n          edges.removeAll(edgesToRemove);\n          exprents.removeAll(exprentsToRemove);\n\n          break;\n        }\n        Statement defaultStatement = statement.getCaseStatements().get(caseIndex);\n        if (defaultStatement.getExprents() != null && defaultStatement.getExprents().size() == 1) {\n          Exprent expectedFastExit = defaultStatement.getExprents().get(0);\n          if (expectedFastExit instanceof ExitExprent exitExprent && exitExprent.getExitType() == EXIT_THROW &&\n              exitExprent.getValue() instanceof NewExprent newExprent && newExprent.getConstructor() != null) {\n            InvocationExprent constructor = newExprent.getConstructor();\n            if (\"java/lang/MatchException\".equals(constructor.getClassName()) && constructor.getParameters().size() == 2 &&\n                constructor.getParameters().get(0) instanceof ConstExprent constExprent1 && constExprent1.isNull() &&\n                constructor.getParameters().get(1) instanceof ConstExprent constExprent2 && constExprent2.isNull()) {\n              deleteDefault = true;\n            }\n          }\n        }\n        break;\n      }\n      if (deleteDefault) {\n        statement.getCaseStatements().remove(indexDefault);\n        statement.getCaseValues().remove(indexDefault);\n        statement.getCaseEdges().remove(indexDefault);\n      }\n    }\n\n    private static void resort(@NotNull SwitchStatement statement, @NotNull List<JavacReferenceFinder.FullCase> cases) {\n      statement.getCaseStatements().clear();\n      statement.getCaseStatements().addAll(cases.stream().map(t -> t.statement).toList());\n      statement.getCaseEdges().clear();\n      statement.getCaseEdges().addAll(cases.stream().map(t -> t.edges).toList());\n      statement.getCaseValues().clear();\n      statement.getCaseValues().addAll(cases.stream().map(t -> t.exprents).toList());\n    }\n  }\n\n  /**\n   * A container class that holds patterns associated with statements.\n   */\n  private static class PatternContainer {\n    private final Map<Statement, List<PatternStatement>> patternsByStatement = new HashMap<>();\n\n    record PatternStatement(@NotNull Statement statement,\n                            @Nullable Exprent guard,\n                            @NotNull Statement caseStatement,\n                            @Nullable VarExprent variable) {\n    }\n\n    void replacePattern(@NotNull Statement statement,\n                        @Nullable Exprent guard,\n                        @NotNull Statement caseStatement,\n                        @Nullable VarExprent assignedName) {\n      PatternStatement patternStatement = new PatternStatement(statement, guard, caseStatement, assignedName);\n      ArrayList<PatternStatement> patterns = new ArrayList<>();\n      patterns.add(patternStatement);\n      patternsByStatement.put(statement, patterns);\n    }\n\n    void addPattern(@NotNull Statement statement,\n                    @Nullable Exprent guard,\n                    @NotNull Statement caseStatement,\n                    @Nullable VarExprent assignedName) {\n      patternsByStatement.compute(statement, (key, value) -> {\n        if (value == null) {\n          value = new ArrayList<>();\n        }\n        value.add(new PatternStatement(key, guard, caseStatement, assignedName));\n        return value;\n      });\n    }\n\n    @Nullable\n    List<PatternStatement> getPatterns(@NotNull Statement statement) {\n      return patternsByStatement.get(statement);\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.decompose;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.List;\n\npublic class DominatorEngine {\n\n  private final Statement statement;\n\n  private final VBStyleCollection<Integer, Integer> colOrderedIDoms = new VBStyleCollection<>();\n\n\n  public DominatorEngine(Statement statement) {\n    this.statement = statement;\n  }\n\n  public void initialize() {\n    calcIDoms();\n  }\n\n  private void orderStatements() {\n\n    for (Statement stat : statement.getReversePostOrderList()) {\n      colOrderedIDoms.addWithKey(null, stat.id);\n    }\n  }\n\n  private static Integer getCommonIDom(Integer key1, Integer key2, VBStyleCollection<Integer, Integer> orderedIDoms) {\n\n    if (key1 == null) {\n      return key2;\n    }\n    else if (key2 == null) {\n      return key1;\n    }\n\n    int index1 = orderedIDoms.getIndexByKey(key1);\n    int index2 = orderedIDoms.getIndexByKey(key2);\n\n    while (index1 != index2) {\n      if (index1 > index2) {\n        key1 = orderedIDoms.getWithKey(key1);\n        index1 = orderedIDoms.getIndexByKey(key1);\n      }\n      else {\n        key2 = orderedIDoms.getWithKey(key2);\n        index2 = orderedIDoms.getIndexByKey(key2);\n      }\n    }\n\n    return key1;\n  }\n\n  private void calcIDoms() {\n\n    orderStatements();\n\n    colOrderedIDoms.putWithKey(statement.getFirst().id, statement.getFirst().id);\n\n    // exclude first statement\n    List<Integer> lstIds = colOrderedIDoms.getLstKeys().subList(1, colOrderedIDoms.getLstKeys().size());\n\n    while (true) {\n\n      boolean changed = false;\n\n      for (Integer id : lstIds) {\n\n        Statement stat = statement.getStats().getWithKey(id);\n        Integer idom = null;\n\n        for (StatEdge edge : stat.getAllPredecessorEdges()) {\n          if (colOrderedIDoms.getWithKey(edge.getSource().id) != null) {\n            idom = getCommonIDom(idom, edge.getSource().id, colOrderedIDoms);\n          }\n        }\n\n        Integer oldidom = colOrderedIDoms.putWithKey(idom, id);\n        if (!idom.equals(oldidom)) {\n          changed = true;\n        }\n      }\n\n      if (!changed) {\n        break;\n      }\n    }\n  }\n\n  public VBStyleCollection<Integer, Integer> getOrderedIDoms() {\n    return colOrderedIDoms;\n  }\n\n  public boolean isDominator(Integer node, Integer dom) {\n\n    while (!node.equals(dom)) {\n\n      Integer idom = colOrderedIDoms.getWithKey(node);\n\n      if (idom.equals(node)) {\n        return false; // root node\n      }\n      else {\n        node = idom;\n      }\n    }\n\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.decompose;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class DominatorTreeExceptionFilter {\n\n  private final Statement statement;\n\n  // idom, nodes\n  private final Map<Integer, Set<Integer>> mapTreeBranches = new HashMap<>();\n\n  // handler, range nodes\n  private final Map<Integer, Set<Integer>> mapExceptionRanges = new HashMap<>();\n\n  // handler, head dom\n  private Map<Integer, Integer> mapExceptionDoms = new HashMap<>();\n\n  // statement, handler, exit nodes\n  private final Map<Integer, Map<Integer, Integer>> mapExceptionRangeUniqueExit = new HashMap<>();\n\n  private DominatorEngine domEngine;\n\n  public DominatorTreeExceptionFilter(Statement statement) {\n    this.statement = statement;\n  }\n\n  public void initialize() {\n    domEngine = new DominatorEngine(statement);\n    domEngine.initialize();\n\n    buildDominatorTree();\n\n    buildExceptionRanges();\n\n    buildFilter(statement.getFirst().id);\n\n    // free resources\n    mapTreeBranches.clear();\n    mapExceptionRanges.clear();\n  }\n\n  public boolean acceptStatementPair(Integer head, Integer exit) {\n    Map<Integer, Integer> filter = mapExceptionRangeUniqueExit.get(head);\n    for (Entry<Integer, Integer> entry : filter.entrySet()) {\n      if (!head.equals(mapExceptionDoms.get(entry.getKey()))) {\n        Integer filterExit = entry.getValue();\n        if (filterExit == -1 || !filterExit.equals(exit)) {\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n\n  private void buildDominatorTree() {\n    VBStyleCollection<Integer, Integer> orderedIDoms = domEngine.getOrderedIDoms();\n\n    List<Integer> lstKeys = orderedIDoms.getLstKeys();\n    for (int index = lstKeys.size() - 1; index >= 0; index--) {\n      Integer key = lstKeys.get(index);\n      Integer idom = orderedIDoms.get(index);\n      mapTreeBranches.computeIfAbsent(idom, k -> new HashSet<>()).add(key);\n    }\n\n    Integer firstid = statement.getFirst().id;\n    mapTreeBranches.get(firstid).remove(firstid);\n  }\n\n  private void buildExceptionRanges() {\n    for (Statement stat : statement.getStats()) {\n      List<Statement> lstPreds = stat.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.BACKWARD);\n      if (!lstPreds.isEmpty()) {\n\n        Set<Integer> set = new HashSet<>();\n\n        for (Statement st : lstPreds) {\n          set.add(st.id);\n        }\n\n        mapExceptionRanges.put(stat.id, set);\n      }\n    }\n\n    mapExceptionDoms = buildExceptionDoms(statement.getFirst().id);\n  }\n\n  private Map<Integer, Integer> buildExceptionDoms(Integer id) {\n    Map<Integer, Integer> map = new HashMap<>();\n\n    Set<Integer> children = mapTreeBranches.get(id);\n    if (children != null) {\n      for (Integer childid : children) {\n        Map<Integer, Integer> mapChild = buildExceptionDoms(childid);\n        for (Integer handler : mapChild.keySet()) {\n          map.put(handler, map.containsKey(handler) ? id : mapChild.get(handler));\n        }\n      }\n    }\n\n    for (Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) {\n      if (entry.getValue().contains(id)) {\n        map.put(entry.getKey(), id);\n      }\n    }\n\n    return map;\n  }\n\n  private void buildFilter(Integer id) {\n    Map<Integer, Integer> map = new HashMap<>();\n\n    Set<Integer> children = mapTreeBranches.get(id);\n    if (children != null) {\n      for (Integer childid : children) {\n        buildFilter(childid);\n\n        Map<Integer, Integer> mapChild = mapExceptionRangeUniqueExit.get(childid);\n        for (Entry<Integer, Set<Integer>> entry : mapExceptionRanges.entrySet()) {\n          Integer handler = entry.getKey();\n          Set<Integer> range = entry.getValue();\n\n          if (range.contains(id)) {\n            Integer exit;\n            if (!range.contains(childid)) {\n              exit = childid;\n            }\n            else if (map.containsKey(handler)) {\n              exit = -1;\n            }\n            else {\n              exit = mapChild.get(handler);\n            }\n\n            if (exit != null) {\n              map.put(handler, exit);\n            }\n          }\n        }\n      }\n    }\n\n    mapExceptionRangeUniqueExit.put(id, map);\n  }\n\n  public DominatorEngine getDomEngine() {\n    return domEngine;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.decompose;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.util.FastFixedSetFactory;\nimport org.jetbrains.java.decompiler.util.FastFixedSetFactory.FastFixedSet;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class FastExtendedPostdominanceHelper {\n\n  private List<Statement> lstReversePostOrderList;\n\n  private HashMap<Integer, FastFixedSet<Integer>> mapSupportPoints = new HashMap<>();\n\n  private final HashMap<Integer, FastFixedSet<Integer>> mapExtPostdominators = new HashMap<>();\n\n  private Statement statement;\n\n  private FastFixedSetFactory<Integer> factory;\n\n  public HashMap<Integer, Set<Integer>> getExtendedPostdominators(Statement statement) {\n\n    this.statement = statement;\n\n    HashSet<Integer> set = new HashSet<>();\n    for (Statement st : statement.getStats()) {\n      set.add(st.id);\n    }\n    this.factory = new FastFixedSetFactory<>(set);\n\n    lstReversePostOrderList = statement.getReversePostOrderList();\n\n    //\t\ttry {\n    //\t\t\tDotExporter.toDotFile(statement, new File(\"c:\\\\Temp\\\\stat1.dot\"));\n    //\t\t} catch (Exception ex) {\n    //\t\t\tex.printStackTrace();\n    //\t\t}\n\n    calcDefaultReachableSets();\n\n    removeErroneousNodes();\n\n    DominatorTreeExceptionFilter filter = new DominatorTreeExceptionFilter(statement);\n    filter.initialize();\n\n    filterOnExceptionRanges(filter);\n\n    filterOnDominance(filter);\n\n    Set<Entry<Integer, FastFixedSet<Integer>>> entries = mapExtPostdominators.entrySet();\n    HashMap<Integer, Set<Integer>> res = new HashMap<>(entries.size());\n    for (Entry<Integer, FastFixedSet<Integer>> entry : entries) {\n      res.put(entry.getKey(), entry.getValue().toPlainSet());\n    }\n\n    return res;\n  }\n\n\n  private void filterOnDominance(DominatorTreeExceptionFilter filter) {\n\n    DominatorEngine engine = filter.getDomEngine();\n\n    for (Integer head : new HashSet<>(mapExtPostdominators.keySet())) {\n\n      FastFixedSet<Integer> setPostdoms = mapExtPostdominators.get(head);\n\n      LinkedList<Statement> stack = new LinkedList<>();\n      LinkedList<FastFixedSet<Integer>> stackPath = new LinkedList<>();\n\n      stack.add(statement.getStats().getWithKey(head));\n      stackPath.add(factory.spawnEmptySet());\n\n      Set<Statement> setVisited = new HashSet<>();\n\n      setVisited.add(stack.getFirst());\n\n      while (!stack.isEmpty()) {\n\n        Statement stat = stack.removeFirst();\n        FastFixedSet<Integer> path = stackPath.removeFirst();\n\n        if (setPostdoms.contains(stat.id)) {\n          path.add(stat.id);\n        }\n\n        if (path.contains(setPostdoms)) {\n          continue;\n        }\n\n        if(!engine.isDominator(stat.id, head)) {\n          setPostdoms.complement(path);\n          continue;\n        }\n\n        for (StatEdge edge : stat.getSuccessorEdges(EdgeType.REGULAR)) {\n\n          Statement edge_destination = edge.getDestination();\n\n          if(!setVisited.contains(edge_destination)) {\n\n            stack.add(edge_destination);\n            stackPath.add(path.getCopy());\n\n            setVisited.add(edge_destination);\n          }\n        }\n      }\n\n      if (setPostdoms.isEmpty()) {\n        mapExtPostdominators.remove(head);\n      }\n    }\n  }\n\n  private void filterOnExceptionRanges(DominatorTreeExceptionFilter filter) {\n    for (Integer head : new HashSet<>(mapExtPostdominators.keySet())) {\n      FastFixedSet<Integer> set = mapExtPostdominators.get(head);\n      for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {\n        if (!filter.acceptStatementPair(head, it.next())) {\n          it.remove();\n        }\n      }\n      if (set.isEmpty()) {\n        mapExtPostdominators.remove(head);\n      }\n    }\n  }\n\n  private void removeErroneousNodes() {\n    mapSupportPoints = new HashMap<>();\n\n    calcReachabilitySuppPoints(EdgeType.REGULAR);\n\n    iterateReachability((node, mapSets) -> {\n      Integer nodeid = node.id;\n\n      FastFixedSet<Integer> setReachability = mapSets.get(nodeid);\n      List<FastFixedSet<Integer>> lstPredSets = new ArrayList<>();\n\n      for (StatEdge prededge : node.getPredecessorEdges(EdgeType.REGULAR)) {\n        FastFixedSet<Integer> setPred = mapSets.get(prededge.getSource().id);\n        if (setPred == null) {\n          setPred = mapSupportPoints.get(prededge.getSource().id);\n        }\n\n        // setPred cannot be empty as it is a reachability set\n        lstPredSets.add(setPred);\n      }\n\n      for (Integer id : setReachability) {\n\n        FastFixedSet<Integer> setReachabilityCopy = setReachability.getCopy();\n\n        FastFixedSet<Integer> setIntersection = factory.spawnEmptySet();\n        boolean isIntersectionInitialized = false;\n\n        for (FastFixedSet<Integer> predset : lstPredSets) {\n          if (predset.contains(id)) {\n            if (!isIntersectionInitialized) {\n              setIntersection.union(predset);\n              isIntersectionInitialized = true;\n            }\n            else {\n              setIntersection.intersection(predset);\n            }\n          }\n        }\n\n        if (nodeid != id.intValue()) {\n          setIntersection.add(nodeid);\n        }\n        else {\n          setIntersection.remove(nodeid);\n        }\n\n        setReachabilityCopy.complement(setIntersection);\n\n        mapExtPostdominators.get(id).complement(setReachabilityCopy);\n      }\n\n      return false;\n    }, EdgeType.REGULAR);\n\n    // exception handlers cannot be postdominator nodes\n    // TODO: replace with a standard set?\n    FastFixedSet<Integer> setHandlers = factory.spawnEmptySet();\n    boolean handlerfound = false;\n\n    for (Statement stat : statement.getStats()) {\n      if (stat.getPredecessorEdges(EdgeType.DIRECT_ALL).isEmpty() &&\n          !stat.getPredecessorEdges(EdgeType.EXCEPTION).isEmpty()) { // exception handler\n        setHandlers.add(stat.id);\n        handlerfound = true;\n      }\n    }\n\n    if (handlerfound) {\n      for (FastFixedSet<Integer> set : mapExtPostdominators.values()) {\n        set.complement(setHandlers);\n      }\n    }\n  }\n\n  private void calcDefaultReachableSets() {\n    EdgeType edgetype = EdgeType.REGULAR.unite(EdgeType.EXCEPTION);\n\n    calcReachabilitySuppPoints(edgetype);\n\n    for (Statement stat : statement.getStats()) {\n      mapExtPostdominators.put(stat.id, factory.spawnEmptySet());\n    }\n\n    iterateReachability((node, mapSets) -> {\n      Integer nodeid = node.id;\n      FastFixedSet<Integer> setReachability = mapSets.get(nodeid);\n\n      for (Integer id : setReachability) {\n        mapExtPostdominators.get(id).add(nodeid);\n      }\n\n      return false;\n    }, edgetype);\n  }\n\n  private void calcReachabilitySuppPoints(final EdgeType edgetype) {\n    iterateReachability((node, mapSets) -> {\n      // consider to be a support point\n      for (StatEdge sucedge : node.getAllSuccessorEdges()) {\n        if ((sucedge.getType().mask() & edgetype.mask()) != 0) {\n          if (mapSets.containsKey(sucedge.getDestination().id)) {\n            FastFixedSet<Integer> setReachability = mapSets.get(node.id);\n\n            if (!Objects.equals(setReachability, mapSupportPoints.get(node.id))) {\n              mapSupportPoints.put(node.id, setReachability);\n              return true;\n            }\n          }\n        }\n      }\n\n      return false;\n    }, edgetype);\n  }\n\n  private void iterateReachability(IReachabilityAction action, EdgeType edgetype) {\n    while (true) {\n      boolean iterate = false;\n\n      HashMap<Integer, FastFixedSet<Integer>> mapSets = new HashMap<>();\n\n      for (Statement stat : lstReversePostOrderList) {\n\n        FastFixedSet<Integer> set = factory.spawnEmptySet();\n        set.add(stat.id);\n\n        for (StatEdge prededge : stat.getAllPredecessorEdges()) {\n          if ((prededge.getType().mask() & edgetype.mask()) != 0) {\n            Statement pred = prededge.getSource();\n\n            FastFixedSet<Integer> setPred = mapSets.get(pred.id);\n            if (setPred == null) {\n              setPred = mapSupportPoints.get(pred.id);\n            }\n\n            if (setPred != null) {\n              set.union(setPred);\n            }\n          }\n        }\n\n        mapSets.put(stat.id, set);\n\n        if (action != null) {\n          iterate |= action.action(stat, mapSets);\n        }\n\n        // remove reachability information of fully processed nodes (saves memory)\n        for (StatEdge prededge : stat.getAllPredecessorEdges()) {\n          if ((prededge.getType().mask() & edgetype.mask()) != 0) {\n            Statement pred = prededge.getSource();\n\n            if (mapSets.containsKey(pred.id)) {\n              boolean remstat = true;\n              for (StatEdge sucedge : pred.getAllSuccessorEdges()) {\n                if ((sucedge.getType().mask() & edgetype.mask()) != 0) {\n                  if (!mapSets.containsKey(sucedge.getDestination().id)) {\n                    remstat = false;\n                    break;\n                  }\n                }\n              }\n\n              if (remstat) {\n                mapSets.put(pred.id, null);\n              }\n            }\n          }\n        }\n      }\n\n      if (!iterate) {\n        break;\n      }\n    }\n  }\n\n\n  private interface IReachabilityAction {\n    boolean action(Statement node, HashMap<Integer, FastFixedSet<Integer>> mapSets);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.decompose;\n\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.List;\nimport java.util.Set;\n\npublic class GenericDominatorEngine {\n\n  private final IGraph graph;\n\n  private final VBStyleCollection<IGraphNode, IGraphNode> colOrderedIDoms = new VBStyleCollection<>();\n\n  private Set<? extends IGraphNode> setRoots;\n\n  public GenericDominatorEngine(IGraph graph) {\n    this.graph = graph;\n  }\n\n  public void initialize() {\n    calcIDoms();\n  }\n\n  private void orderNodes() {\n\n    setRoots = graph.getRoots();\n\n    for (IGraphNode node : graph.getReversePostOrderList()) {\n      colOrderedIDoms.addWithKey(null, node);\n    }\n  }\n\n  private static IGraphNode getCommonIDom(IGraphNode node1, IGraphNode node2, VBStyleCollection<IGraphNode, IGraphNode> orderedIDoms) {\n\n    IGraphNode nodeOld;\n\n    if (node1 == null) {\n      return node2;\n    }\n    else if (node2 == null) {\n      return node1;\n    }\n\n    int index1 = orderedIDoms.getIndexByKey(node1);\n    int index2 = orderedIDoms.getIndexByKey(node2);\n\n    while (index1 != index2) {\n      if (index1 > index2) {\n        nodeOld = node1;\n        node1 = orderedIDoms.getWithKey(node1);\n\n        if (nodeOld == node1) { // no idom - root or merging point\n          return null;\n        }\n\n        index1 = orderedIDoms.getIndexByKey(node1);\n      }\n      else {\n        nodeOld = node2;\n        node2 = orderedIDoms.getWithKey(node2);\n\n        if (nodeOld == node2) { // no idom - root or merging point\n          return null;\n        }\n\n        index2 = orderedIDoms.getIndexByKey(node2);\n      }\n    }\n\n    return node1;\n  }\n\n  private void calcIDoms() {\n\n    orderNodes();\n\n    List<IGraphNode> lstNodes = colOrderedIDoms.getLstKeys();\n\n    while (true) {\n\n      boolean changed = false;\n\n      for (IGraphNode node : lstNodes) {\n\n        IGraphNode idom = null;\n\n        if (!setRoots.contains(node)) {\n          for (IGraphNode pred : node.getPredecessorNodes()) {\n            if (colOrderedIDoms.getWithKey(pred) != null) {\n              idom = getCommonIDom(idom, pred, colOrderedIDoms);\n              if (idom == null) {\n                break; // no idom found: merging point of two trees\n              }\n            }\n          }\n        }\n\n        if (idom == null) {\n          idom = node;\n        }\n\n        IGraphNode oldidom = colOrderedIDoms.putWithKey(idom, node);\n        if (!idom.equals(oldidom)) { // oldidom is null iff the node is touched for the first time\n          changed = true;\n        }\n      }\n\n      if (!changed) {\n        break;\n      }\n    }\n  }\n\n  public boolean isDominator(IGraphNode node, IGraphNode dom) {\n\n    while (!node.equals(dom)) {\n\n      IGraphNode idom = colOrderedIDoms.getWithKey(node);\n\n      if (idom == node) {\n        return false; // root node or merging point\n      }\n      else if (idom == null) {\n        throw new RuntimeException(\"Inconsistent idom sequence discovered!\");\n      }\n      else {\n        node = idom;\n      }\n    }\n\n    return true;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.decompose;\n\nimport java.util.List;\nimport java.util.Set;\n\npublic interface IGraph {\n\n  List<? extends IGraphNode> getReversePostOrderList();\n\n  Set<? extends IGraphNode> getRoots();\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.decompose;\n\nimport java.util.List;\n\npublic interface IGraphNode {\n  List<? extends IGraphNode> getPredecessorNodes();\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.deobfuscator;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.code.SimpleInstructionSequence;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;\nimport org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine;\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;\nimport org.jetbrains.java.decompiler.struct.StructClass;\n\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\npublic final class ExceptionDeobfuscator {\n\n  private static final class Range {\n    private final BasicBlock handler;\n    private final String uniqueStr;\n    private final Set<BasicBlock> protectedRange;\n    private final ExceptionRangeCFG rangeCFG;\n\n    private Range(BasicBlock handler, String uniqueStr, Set<BasicBlock> protectedRange, ExceptionRangeCFG rangeCFG) {\n      this.handler = handler;\n      this.uniqueStr = uniqueStr;\n      this.protectedRange = protectedRange;\n      this.rangeCFG = rangeCFG;\n    }\n  }\n\n  public static void restorePopRanges(ControlFlowGraph graph) {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n\n    List<Range> lstRanges = new ArrayList<>();\n\n    // aggregate ranges\n    for (ExceptionRangeCFG range : graph.getExceptions()) {\n      boolean found = false;\n      for (Range arr : lstRanges) {\n        if (arr.handler == range.getHandler() && Objects.equals(range.getUniqueExceptionsString(), arr.uniqueStr)) {\n          arr.protectedRange.addAll(range.getProtectedRange());\n          found = true;\n          break;\n        }\n      }\n\n      if (!found) {\n        // doesn't matter, which range chosen\n        lstRanges.add(new Range(range.getHandler(), range.getUniqueExceptionsString(), new HashSet<>(range.getProtectedRange()), range));\n      }\n    }\n\n    // process aggregated ranges\n    for (Range range : lstRanges) {\n\n      if (range.uniqueStr != null) {\n\n        BasicBlock handler = range.handler;\n        InstructionSequence seq = handler.getSeq();\n\n        Instruction firstinstr;\n        if (!seq.isEmpty()) {\n          firstinstr = seq.getInstr(0);\n\n          if (firstinstr.opcode == CodeConstants.opc_pop ||\n              firstinstr.opcode == CodeConstants.opc_astore) {\n            Set<BasicBlock> setrange = new HashSet<>(range.protectedRange);\n\n            for (Range range_super : lstRanges) { // finally or strict superset\n              cancellationManager.checkCanceled();\n              if (range != range_super) {\n\n                Set<BasicBlock> setrange_super = new HashSet<>(range_super.protectedRange);\n\n                if (!setrange.contains(range_super.handler) && !setrange_super.contains(handler)\n                    && (range_super.uniqueStr == null || setrange_super.containsAll(setrange))) {\n\n                  if (range_super.uniqueStr == null) {\n                    setrange_super.retainAll(setrange);\n                  }\n                  else {\n                    setrange_super.removeAll(setrange);\n                  }\n\n                  if (!setrange_super.isEmpty()) {\n\n                    BasicBlock newBlock = handler;\n\n                    // split the handler\n                    if (seq.length() > 1) {\n                      InstructionSequence newSeq = new SimpleInstructionSequence();\n                      newSeq.addInstruction(firstinstr.clone(), -1);\n                      newBlock = new BasicBlock(++graph.last_id, newSeq);\n                      graph.getBlocks().addWithKey(newBlock, newBlock.id);\n\n                      List<BasicBlock> lstTemp = new ArrayList<>();\n                      lstTemp.addAll(handler.getPredecessors());\n                      lstTemp.addAll(handler.getPredecessorExceptions());\n\n                      // replace predecessors\n                      for (BasicBlock pred : lstTemp) {\n                        pred.replaceSuccessor(handler, newBlock);\n                      }\n\n                      // replace handler\n                      for (ExceptionRangeCFG range_ext : graph.getExceptions()) {\n                        if (range_ext.getHandler() == handler) {\n                          range_ext.setHandler(newBlock);\n                        }\n                        else if (range_ext.getProtectedRange().contains(handler)) {\n                          newBlock.addSuccessorException(range_ext.getHandler());\n                          range_ext.getProtectedRange().add(newBlock);\n                        }\n                      }\n\n                      newBlock.addSuccessor(handler);\n                      if (graph.getFirst() == handler) {\n                        graph.setFirst(newBlock);\n                      }\n\n                      // remove the first pop in the handler\n                      seq.removeInstruction(0);\n                    }\n\n                    newBlock.addSuccessorException(range_super.handler);\n                    range_super.rangeCFG.getProtectedRange().add(newBlock);\n\n                    handler = range.rangeCFG.getHandler();\n                    seq = handler.getSeq();\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n\n  public static void insertEmptyExceptionHandlerBlocks(ControlFlowGraph graph) {\n\n    Set<BasicBlock> setVisited = new HashSet<>();\n\n    for (ExceptionRangeCFG range : graph.getExceptions()) {\n      BasicBlock handler = range.getHandler();\n\n      if (setVisited.contains(handler)) {\n        continue;\n      }\n      setVisited.add(handler);\n\n      BasicBlock emptyblock = new BasicBlock(++graph.last_id);\n      graph.getBlocks().addWithKey(emptyblock, emptyblock.id);\n\n      // only exception predecessors considered\n      List<BasicBlock> lstTemp = new ArrayList<>(handler.getPredecessorExceptions());\n\n      // replace predecessors\n      for (BasicBlock pred : lstTemp) {\n        pred.replaceSuccessor(handler, emptyblock);\n      }\n\n      // replace handler\n      for (ExceptionRangeCFG range_ext : graph.getExceptions()) {\n        if (range_ext.getHandler() == handler) {\n          range_ext.setHandler(emptyblock);\n        }\n        else if (range_ext.getProtectedRange().contains(handler)) {\n          emptyblock.addSuccessorException(range_ext.getHandler());\n          range_ext.getProtectedRange().add(emptyblock);\n        }\n      }\n\n      emptyblock.addSuccessor(handler);\n      if (graph.getFirst() == handler) {\n        graph.setFirst(emptyblock);\n      }\n    }\n  }\n\n  public static void removeEmptyRanges(ControlFlowGraph graph) {\n\n    List<ExceptionRangeCFG> lstRanges = graph.getExceptions();\n    for (int i = lstRanges.size() - 1; i >= 0; i--) {\n      ExceptionRangeCFG range = lstRanges.get(i);\n\n      boolean isEmpty = true;\n      for (BasicBlock block : range.getProtectedRange()) {\n        if (!block.getSeq().isEmpty()) {\n          isEmpty = false;\n          break;\n        }\n      }\n\n      if (isEmpty) {\n        for (BasicBlock block : range.getProtectedRange()) {\n          block.removeSuccessorException(range.getHandler());\n        }\n\n        lstRanges.remove(i);\n      }\n    }\n  }\n\n  public static void removeCircularRanges(final ControlFlowGraph graph) {\n\n    GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() {\n      @Override\n      public List<? extends IGraphNode> getReversePostOrderList() {\n        return graph.getReversePostOrder();\n      }\n\n      @Override\n      public Set<? extends IGraphNode> getRoots() {\n        return new HashSet<>(Collections.singletonList(graph.getFirst()));\n      }\n    });\n\n    engine.initialize();\n\n    List<ExceptionRangeCFG> lstRanges = graph.getExceptions();\n    for (int i = lstRanges.size() - 1; i >= 0; i--) {\n      ExceptionRangeCFG range = lstRanges.get(i);\n\n      BasicBlock handler = range.getHandler();\n      List<BasicBlock> rangeList = range.getProtectedRange();\n\n      if (rangeList.contains(handler)) {  // TODO: better removing strategy\n\n        List<BasicBlock> lstRemBlocks = getReachableBlocksRestricted(range.getHandler(), range, engine);\n\n        if (lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) {\n          for (BasicBlock block : lstRemBlocks) {\n            block.removeSuccessorException(handler);\n            rangeList.remove(block);\n          }\n        }\n\n        if (rangeList.isEmpty()) {\n          lstRanges.remove(i);\n        }\n      }\n    }\n  }\n\n  private static List<BasicBlock> getReachableBlocksRestricted(BasicBlock start, ExceptionRangeCFG range, GenericDominatorEngine engine) {\n\n    List<BasicBlock> lstRes = new ArrayList<>();\n\n    LinkedList<BasicBlock> stack = new LinkedList<>();\n    Set<BasicBlock> setVisited = new HashSet<>();\n\n    stack.addFirst(start);\n\n    while (!stack.isEmpty()) {\n      BasicBlock block = stack.removeFirst();\n\n      setVisited.add(block);\n\n      if (range.getProtectedRange().contains(block) && engine.isDominator(block, start)) {\n        lstRes.add(block);\n\n        List<BasicBlock> lstSuccs = new ArrayList<>(block.getSuccessors());\n        lstSuccs.addAll(block.getSuccessorExceptions());\n\n        for (BasicBlock succ : lstSuccs) {\n          if (!setVisited.contains(succ)) {\n            stack.add(succ);\n          }\n        }\n      }\n    }\n\n    return lstRes;\n  }\n\n  public static boolean hasObfuscatedExceptions(ControlFlowGraph graph) {\n    Map<BasicBlock, Set<BasicBlock>> mapRanges = new HashMap<>();\n    for (ExceptionRangeCFG range : graph.getExceptions()) {\n      mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).addAll(range.getProtectedRange());\n    }\n\n    for (Entry<BasicBlock, Set<BasicBlock>> ent : mapRanges.entrySet()) {\n      Set<BasicBlock> setEntries = new HashSet<>();\n\n      for (BasicBlock block : ent.getValue()) {\n        Set<BasicBlock> setTemp = new HashSet<>(block.getPredecessors());\n        setTemp.removeAll(ent.getValue());\n\n        if (!setTemp.isEmpty()) {\n          setEntries.add(block);\n        }\n      }\n\n      if (!setEntries.isEmpty()) {\n        if (setEntries.size() > 1 /*|| ent.getValue().contains(first)*/) {\n          return true;\n        }\n      }\n    }\n\n    return false;\n  }\n\n  public static boolean handleMultipleEntryExceptionRanges(ControlFlowGraph graph) {\n    GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() {\n      @Override\n      public List<? extends IGraphNode> getReversePostOrderList() {\n        return graph.getReversePostOrder();\n      }\n\n      @Override\n      public Set<? extends IGraphNode> getRoots() {\n        return new HashSet<>(Collections.singletonList(graph.getFirst()));\n      }\n    });\n\n    engine.initialize();\n\n    boolean found;\n\n    while (true) {\n      found = false;\n      boolean splitted = false;\n\n      for (ExceptionRangeCFG range : graph.getExceptions()) {\n        Set<BasicBlock> setEntries = getRangeEntries(range);\n\n        if (setEntries.size() > 1) { // multiple-entry protected range\n          found = true;\n\n          if (splitExceptionRange(range, setEntries, graph, engine)) {\n            splitted = true;\n            break;\n          }\n        }\n      }\n\n      if (!splitted) {\n        break;\n      }\n    }\n\n    return !found;\n  }\n\n  private static Set<BasicBlock> getRangeEntries(ExceptionRangeCFG range) {\n    Set<BasicBlock> setEntries = new HashSet<>();\n    Set<BasicBlock> setRange = new HashSet<>(range.getProtectedRange());\n\n    for (BasicBlock block : range.getProtectedRange()) {\n      Set<BasicBlock> setPreds = new HashSet<>(block.getPredecessors());\n      setPreds.removeAll(setRange);\n\n      if (!setPreds.isEmpty()) {\n        setEntries.add(block);\n      }\n    }\n\n    return setEntries;\n  }\n\n  private static boolean splitExceptionRange(ExceptionRangeCFG range,\n                                             Set<BasicBlock> setEntries,\n                                             ControlFlowGraph graph,\n                                             GenericDominatorEngine engine) {\n    for (BasicBlock entry : setEntries) {\n      List<BasicBlock> lstSubrangeBlocks = getReachableBlocksRestricted(entry, range, engine);\n      if (!lstSubrangeBlocks.isEmpty() && lstSubrangeBlocks.size() < range.getProtectedRange().size()) {\n        // add new range\n        ExceptionRangeCFG subRange = new ExceptionRangeCFG(lstSubrangeBlocks, range.getHandler(), range.getExceptionTypes());\n        graph.getExceptions().add(subRange);\n        // shrink the original range\n        range.getProtectedRange().removeAll(lstSubrangeBlocks);\n        return true;\n      }\n      else {\n        // should not happen\n        DecompilerContext.getLogger().writeMessage(\"Inconsistency found while splitting protected range\", IFernflowerLogger.Severity.WARN);\n      }\n    }\n\n    return false;\n  }\n\n  /**\n   * Duplicates merged catch blocks; it needs to process files with record pattern matching,\n   * because the javac compiler collapses all catch blocks for same exception into one block.\n   * It breaks logic to find a possible way. It must be called before other optimizations; otherwise\n   * this decompiler will add a lot of empty blocks, which cannot be processed correctly.\n   *\n   * @param graph The control flow graph containing the merged catch blocks.\n   * @param cl    The class containing the control flow graph.\n   */\n  public static void duplicateMergedCatchBlocks(@NotNull ControlFlowGraph graph, @NotNull StructClass cl) {\n    if (!cl.hasRecordPatternSupport()) {\n      return;\n    }\n    Map<BasicBlock, Set<ExceptionRangeCFG>> mapRanges = new HashMap<>();\n    for (ExceptionRangeCFG range : graph.getExceptions()) {\n      mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).add(range);\n    }\n\n    for (Entry<BasicBlock, Set<ExceptionRangeCFG>> ent : mapRanges.entrySet()) {\n      BasicBlock handler = ent.getKey();\n      Set<ExceptionRangeCFG> ranges = ent.getValue();\n\n      if (ranges.size() == 1) {\n        continue;\n      }\n      if (handler.getLastInstruction().opcode != CodeConstants.opc_athrow) {\n        continue;\n      }\n      Set<String> exceptions = ranges.stream()\n        .map(t -> t.getExceptionTypes())\n        .flatMap(t -> t != null ? t.stream() : Stream.empty())\n        .collect(Collectors.toSet());\n      List<BasicBlock> successors = handler.getSuccessors();\n      if (successors != null && successors.size() == 1 && successors.get(0).getSuccessors().isEmpty() &&\n          successors.get(0).getSuccessorExceptions().isEmpty() &&\n          //exceptions contain only one type of exceptions, and it is not null, because null defines `finally` blocks\n          exceptions.size() == 1 && !exceptions.contains(null)) {\n        for (ExceptionRangeCFG range : ranges) {\n          BasicBlock newHandler = handler.clone(++graph.last_id);\n          graph.getBlocks().addWithKey(newHandler, newHandler.id);\n          // only exception predecessors from this range considered\n          List<BasicBlock> lstPredExceptions = new ArrayList<>(handler.getPredecessorExceptions());\n          lstPredExceptions.retainAll(range.getProtectedRange());\n          // replace predecessors\n          for (BasicBlock pred : lstPredExceptions) {\n            ExceptionRangeCFG previousEdge = graph.getExceptionRange(handler, pred);\n            pred.replaceSuccessor(handler, newHandler);\n            if (previousEdge != null) {\n              previousEdge.setHandler(newHandler);\n            }\n          }\n          // replace successors\n          List<BasicBlock> scExceptions = new ArrayList<>(handler.getSuccessorExceptions());\n          for (BasicBlock nextException : scExceptions) {\n            ExceptionRangeCFG nextEdge = graph.getExceptionRange(nextException, handler);\n            newHandler.addSuccessorException(nextException);\n            nextEdge.getProtectedRange().add(newHandler);\n          }\n          //add fast exit\n          newHandler.addSuccessor(successors.get(0));\n          for (BasicBlock successorException : handler.getSuccessorExceptions()) {\n            newHandler.addSuccessorException(successorException);\n            ExceptionRangeCFG previousEdge = graph.getExceptionRange(successorException, handler);\n            if (previousEdge != null) {\n              ArrayList<BasicBlock> newRanges = new ArrayList<>();\n              newRanges.add(newHandler);\n              ExceptionRangeCFG subRange = new ExceptionRangeCFG(newRanges, successorException, previousEdge.getExceptionTypes());\n              graph.getExceptions().add(subRange);\n              successorException.addPredecessorException(newHandler);\n            }\n          }\n          range.setHandler(newHandler);\n        }\n\n        for (BasicBlock successorException : handler.getSuccessorExceptions()) {\n          successorException.removePredecessorException(handler);\n          if (successorException.getPredecessorExceptions().isEmpty()) {\n            graph.removeBlock(successorException);\n          }\n        }\n        graph.removeBlock(handler);\n      }\n    }\n  }\n\n  public static void insertDummyExceptionHandlerBlocks(ControlFlowGraph graph, int bytecode_version) {\n    Map<BasicBlock, Set<ExceptionRangeCFG>> mapRanges = new HashMap<>();\n    for (ExceptionRangeCFG range : graph.getExceptions()) {\n      mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).add(range);\n    }\n\n    for (Entry<BasicBlock, Set<ExceptionRangeCFG>> ent : mapRanges.entrySet()) {\n      BasicBlock handler = ent.getKey();\n      Set<ExceptionRangeCFG> ranges = ent.getValue();\n\n      if (ranges.size() == 1) {\n        continue;\n      }\n\n      for (ExceptionRangeCFG range : ranges) {\n        // add some dummy instructions to prevent optimizing away the empty block\n        SimpleInstructionSequence seq = new SimpleInstructionSequence();\n        seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1);\n        seq.addInstruction(Instruction.create(CodeConstants.opc_pop, false, CodeConstants.GROUP_GENERAL, bytecode_version, null), -1);\n\n        BasicBlock dummyBlock = new BasicBlock(++graph.last_id, seq);\n\n        graph.getBlocks().addWithKey(dummyBlock, dummyBlock.id);\n\n        // only exception predecessors from this range considered\n        List<BasicBlock> lstPredExceptions = new ArrayList<>(handler.getPredecessorExceptions());\n        lstPredExceptions.retainAll(range.getProtectedRange());\n\n        // replace predecessors\n        for (BasicBlock pred : lstPredExceptions) {\n          ExceptionRangeCFG previousEdge = graph.getExceptionRange(handler, pred);\n          pred.replaceSuccessor(handler, dummyBlock);\n          if (previousEdge != null) {\n            previousEdge.setHandler(dummyBlock);\n          }\n        }\n\n        // replace handler\n        range.setHandler(dummyBlock);\n        // add common exception edges\n        Set<BasicBlock> commonHandlers = new HashSet<>(handler.getSuccessorExceptions());\n        for (BasicBlock pred : lstPredExceptions) {\n          commonHandlers.retainAll(pred.getSuccessorExceptions());\n        }\n        // TODO: more sanity checks?\n        for (BasicBlock commonHandler : commonHandlers) {\n          ExceptionRangeCFG commonRange = graph.getExceptionRange(commonHandler, handler);\n\n          dummyBlock.addSuccessorException(commonHandler);\n          commonRange.getProtectedRange().add(dummyBlock);\n        }\n\n        dummyBlock.addSuccessor(handler);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.deobfuscator;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Set;\n\n\npublic final class IrreducibleCFGDeobfuscator {\n\n\n  public static boolean isStatementIrreducible(Statement statement) {\n\n    class Node {\n      public final Integer id;\n      public final Set<Node> preds = new HashSet<>();\n      public final Set<Node> succs = new HashSet<>();\n\n      Node(Integer id) {\n        this.id = id;\n      }\n    }\n\n    HashMap<Integer, Node> mapNodes = new HashMap<>();\n\n    // checking exceptions and creating nodes\n    for (Statement stat : statement.getStats()) {\n      if (!stat.getSuccessorEdges(EdgeType.EXCEPTION).isEmpty()) {\n        return false;\n      }\n\n      mapNodes.put(stat.id, new Node(stat.id));\n    }\n\n    // connecting nodes\n    for (Statement stat : statement.getStats()) {\n      Node node = mapNodes.get(stat.id);\n\n      for (Statement succ : stat.getNeighbours(EdgeType.REGULAR, EdgeDirection.FORWARD)) {\n        Node nodeSucc = mapNodes.get(succ.id);\n\n        node.succs.add(nodeSucc);\n        nodeSucc.preds.add(node);\n      }\n    }\n\n    // transforming and reducing the graph\n    while (true) {\n      int ttype = 0;\n      Node node = null;\n\n      for (Node nd : mapNodes.values()) {\n        if (nd.succs.contains(nd)) { // T1\n          ttype = 1;\n        }\n        else if (nd.preds.size() == 1) { // T2\n          ttype = 2;\n        }\n\n        if (ttype != 0) {\n          node = nd;\n          break;\n        }\n      }\n\n      if (node != null) {\n        if (ttype == 1) {\n          node.succs.remove(node);\n          node.preds.remove(node);\n        }\n        else {\n          Node pred = node.preds.iterator().next();\n\n          pred.succs.addAll(node.succs);\n          pred.succs.remove(node);\n\n          for (Node succ : node.succs) {\n            succ.preds.remove(node);\n            succ.preds.add(pred);\n          }\n\n          mapNodes.remove(node.id);\n        }\n      }\n      else { // no transformation applicable\n        return mapNodes.size() > 1; // reducible iff one node remains\n      }\n    }\n  }\n\n\n  private static Statement getCandidateForSplitting(Statement statement) {\n\n    Statement candidateForSplitting = null;\n    int sizeCandidateForSplitting = Integer.MAX_VALUE;\n    int succsCandidateForSplitting = Integer.MAX_VALUE;\n\n    for (Statement stat : statement.getStats()) {\n\n      Set<Statement> setPreds = stat.getNeighboursSet(EdgeType.REGULAR, EdgeDirection.BACKWARD);\n\n      if (setPreds.size() > 1) {\n        int succCount = stat.getNeighboursSet(EdgeType.REGULAR, EdgeDirection.FORWARD).size();\n        if (succCount <= succsCandidateForSplitting) {\n          int size = getStatementSize(stat) * (setPreds.size() - 1);\n\n          if (succCount < succsCandidateForSplitting ||\n              size < sizeCandidateForSplitting) {\n            candidateForSplitting = stat;\n            sizeCandidateForSplitting = size;\n            succsCandidateForSplitting = succCount;\n          }\n        }\n      }\n    }\n\n    return candidateForSplitting;\n  }\n\n  public static boolean splitIrreducibleNode(Statement statement) {\n\n    Statement splitnode = getCandidateForSplitting(statement);\n    if (splitnode == null) {\n      return false;\n    }\n\n    StatEdge enteredge = splitnode.getPredecessorEdges(EdgeType.REGULAR).iterator().next();\n\n    // copy the smallest statement\n    Statement splitcopy = copyStatement(splitnode, null, new HashMap<>());\n    initCopiedStatement(splitcopy);\n\n    // insert the copy\n    splitcopy.setParent(statement);\n    statement.getStats().addWithKey(splitcopy, splitcopy.id);\n\n    // switch input edges\n    for (StatEdge prededge : splitnode.getPredecessorEdges(EdgeType.DIRECT_ALL)) {\n      if (prededge.getSource() == enteredge.getSource() ||\n          prededge.closure == enteredge.getSource()) {\n        splitnode.removePredecessor(prededge);\n        prededge.getSource().changeEdgeNode(EdgeDirection.FORWARD, prededge, splitcopy);\n        splitcopy.addPredecessor(prededge);\n      }\n    }\n\n    // connect successors\n    for (StatEdge succ : splitnode.getSuccessorEdges(EdgeType.DIRECT_ALL)) {\n      splitcopy.addSuccessor(new StatEdge(succ.getType(), splitcopy, succ.getDestination(), succ.closure));\n    }\n\n    return true;\n  }\n\n  private static int getStatementSize(Statement statement) {\n\n    int res;\n\n    if (statement.type == StatementType.BASIC_BLOCK) {\n      res = ((BasicBlockStatement)statement).getBlock().getSeq().length();\n    }\n    else {\n      res = statement.getStats().stream().mapToInt(IrreducibleCFGDeobfuscator::getStatementSize).sum();\n    }\n\n    return res;\n  }\n\n  private static Statement copyStatement(Statement from, Statement to, HashMap<Statement, Statement> mapAltToCopies) {\n\n    if (to == null) {\n      // first outer invocation\n      to = from.getSimpleCopy();\n      mapAltToCopies.put(from, to);\n    }\n\n    // copy statements\n    for (Statement st : from.getStats()) {\n      Statement stcopy = st.getSimpleCopy();\n\n      to.getStats().addWithKey(stcopy, stcopy.id);\n      mapAltToCopies.put(st, stcopy);\n    }\n\n    // copy edges\n    for (int i = 0; i < from.getStats().size(); i++) {\n      Statement stold = from.getStats().get(i);\n      Statement stnew = to.getStats().get(i);\n\n      for (StatEdge edgeold : stold.getSuccessorEdges(EdgeType.DIRECT_ALL)) {\n        // type cannot be TYPE_EXCEPTION (checked in isIrreducibleTriangle)\n        StatEdge edgenew = new StatEdge(edgeold.getType(), stnew,\n                                        mapAltToCopies.containsKey(edgeold.getDestination())\n                                        ? mapAltToCopies.get(edgeold.getDestination())\n                                        : edgeold.getDestination(),\n                                        mapAltToCopies.containsKey(edgeold.closure)\n                                        ? mapAltToCopies.get(edgeold.closure)\n                                        : edgeold.closure);\n\n        stnew.addSuccessor(edgenew);\n      }\n    }\n\n    // recurse statements\n    for (int i = 0; i < from.getStats().size(); i++) {\n      Statement stold = from.getStats().get(i);\n      Statement stnew = to.getStats().get(i);\n\n      copyStatement(stold, stnew, mapAltToCopies);\n    }\n\n    return to;\n  }\n\n  private static void initCopiedStatement(Statement statement) {\n\n    statement.initSimpleCopy();\n    statement.setCopied(true);\n\n    for (Statement st : statement.getStats()) {\n      st.setParent(statement);\n      initCopiedStatement(st);\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.List;\nimport java.util.Objects;\n\npublic class AnnotationExprent extends Exprent {\n  public static final int ANNOTATION_NORMAL = 1;\n  public static final int ANNOTATION_MARKER = 2;\n  public static final int ANNOTATION_SINGLE_ELEMENT = 3;\n\n  private final String className;\n  private final List<String> parNames;\n  private final List<? extends Exprent> parValues;\n\n  public AnnotationExprent(String className, List<String> parNames, List<? extends Exprent> parValues) {\n    super(EXPRENT_ANNOTATION);\n    this.className = className;\n    this.parNames = parNames;\n    this.parValues = parValues;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buffer = new TextBuffer();\n\n    buffer.appendIndent(indent);\n    buffer.append('@');\n    buffer.append(DecompilerContext.getImportCollector().getNestedName(ExprProcessor.buildJavaClassName(className)));\n\n    int type = getAnnotationType();\n\n    if (type != ANNOTATION_MARKER) {\n      buffer.append('(');\n\n      boolean oneLiner = type == ANNOTATION_SINGLE_ELEMENT || indent < 0;\n\n      for (int i = 0; i < parNames.size(); i++) {\n        if (!oneLiner) {\n          buffer.appendLineSeparator().appendIndent(indent + 1);\n        }\n\n        if (type != ANNOTATION_SINGLE_ELEMENT) {\n          buffer.append(parNames.get(i));\n          buffer.append(\" = \");\n        }\n\n        buffer.append(parValues.get(i).toJava(0, tracer));\n\n        if (i < parNames.size() - 1) {\n          buffer.append(',');\n        }\n      }\n\n      if (!oneLiner) {\n        buffer.appendLineSeparator().appendIndent(indent);\n      }\n\n      buffer.append(')');\n    }\n\n    return buffer;\n  }\n\n  public String getClassName() {\n    return className;\n  }\n\n  public int getAnnotationType() {\n    if (parNames.isEmpty()) {\n      return ANNOTATION_MARKER;\n    }\n    else if (parNames.size() == 1 && \"value\".equals(parNames.get(0))) {\n      return ANNOTATION_SINGLE_ELEMENT;\n    }\n    else {\n      return ANNOTATION_NORMAL;\n    }\n  }\n\n  @Override\n  public int hashCode() {\n    return Objects.hash(className, parNames, parValues);\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof AnnotationExprent ann)) return false;\n\n    return className.equals(ann.className) &&\n           parNames.equals(ann.parNames) &&\n           parValues.equals(ann.parValues);\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\n\npublic class ArrayExprent extends Exprent {\n  private Exprent array;\n  private Exprent index;\n  private final VarType hardType;\n\n  public ArrayExprent(Exprent array, Exprent index, VarType hardType, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_ARRAY);\n    this.array = array;\n    this.index = index;\n    this.hardType = hardType;\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  @Override\n  public Exprent copy() {\n    return new ArrayExprent(array.copy(), index.copy(), hardType, bytecode);\n  }\n\n  @Override\n  public VarType getExprType() {\n    VarType exprType = array.getExprType();\n    if (exprType.equals(VarType.VARTYPE_NULL)) {\n      return hardType.copy();\n    }\n    else {\n      return exprType.decreaseArrayDim();\n    }\n  }\n\n  @Override\n  public int getExprentUse() {\n    return array.getExprentUse() & index.getExprentUse() & Exprent.MULTIPLE_USES;\n  }\n\n  @Override\n  public CheckTypesResult checkExprTypeBounds() {\n    CheckTypesResult result = new CheckTypesResult();\n    result.addMinTypeExprent(index, VarType.VARTYPE_BYTECHAR);\n    result.addMaxTypeExprent(index, VarType.VARTYPE_INT);\n    return result;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n    lst.add(array);\n    lst.add(index);\n    return lst;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer res = array.toJava(indent, tracer);\n\n    if (array.getPrecedence() > getPrecedence()) { // array precedence equals 0\n      res.enclose(\"(\", \")\");\n    }\n\n    VarType arrType = array.getExprType();\n    if (arrType.getArrayDim() == 0) {\n      VarType objArr = VarType.VARTYPE_OBJECT.resizeArrayDim(1); // type family does not change\n      res.enclose(\"((\" + ExprProcessor.getCastTypeName(objArr, Collections.emptyList()) + \")\", \")\");\n    }\n\n    tracer.addMapping(bytecode);\n\n    return res.append('[').append(index.toJava(indent, tracer)).append(']');\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == array) {\n      array = newExpr;\n    }\n    if (oldExpr == index) {\n      index = newExpr;\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof ArrayExprent arr)) return false;\n\n    return Objects.equals(array, arr.getArray()) &&\n           Objects.equals(index, arr.getIndex());\n  }\n\n  public Exprent getArray() {\n    return array;\n  }\n\n  public Exprent getIndex() {\n    return index;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java",
    "content": "/*\n * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\n */\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.util.TextBuffer;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\n\nimport java.util.List;\n\npublic class AssertExprent extends Exprent {\n\n  private final List<? extends Exprent> parameters;\n\n  public AssertExprent(List<? extends Exprent> parameters) {\n    super(EXPRENT_ASSERT);\n    this.parameters = parameters;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buffer = new TextBuffer();\n\n    buffer.append(\"assert \");\n\n    tracer.addMapping(bytecode);\n\n    if (parameters.get(0) == null) {\n      buffer.append(\"false\");\n    }\n    else {\n      buffer.append(parameters.get(0).toJava(indent, tracer));\n    }\n\n    if (parameters.size() > 1) {\n      buffer.append(\" : \");\n      buffer.append(parameters.get(1).toJava(indent, tracer));\n    }\n\n    return buffer;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\n\npublic class AssignmentExprent extends Exprent {\n\n  public static final int CONDITION_NONE = -1;\n\n  private static final String[] OPERATORS = {\n    \" += \",   // FUNCTION_ADD\n    \" -= \",   // FUNCTION_SUB\n    \" *= \",   // FUNCTION_MUL\n    \" /= \",   // FUNCTION_DIV\n    \" &= \",   // FUNCTION_AND\n    \" |= \",   // FUNCTION_OR\n    \" ^= \",   // FUNCTION_XOR\n    \" %= \",   // FUNCTION_REM\n    \" <<= \",  // FUNCTION_SHL\n    \" >>= \",  // FUNCTION_SHR\n    \" >>>= \"  // FUNCTION_USHR\n  };\n\n  private Exprent left;\n  private Exprent right;\n  private int condType = CONDITION_NONE;\n\n  public AssignmentExprent(Exprent left, Exprent right, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_ASSIGNMENT);\n    this.left = left;\n    this.right = right;\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  @Override\n  public VarType getExprType() {\n    return left.getExprType();\n  }\n\n  @Override\n  public CheckTypesResult checkExprTypeBounds() {\n    CheckTypesResult result = new CheckTypesResult();\n\n    VarType typeLeft = left.getExprType();\n    VarType typeRight = right.getExprType();\n\n    if (typeLeft.getTypeFamily() > typeRight.getTypeFamily()) {\n      result.addMinTypeExprent(right, VarType.getMinTypeInFamily(typeLeft.getTypeFamily()));\n    }\n    else if (typeLeft.getTypeFamily() < typeRight.getTypeFamily()) {\n      result.addMinTypeExprent(left, typeRight);\n    }\n    else {\n      result.addMinTypeExprent(left, VarType.getCommonSupertype(typeLeft, typeRight));\n    }\n\n    return result;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n    lst.add(left);\n    lst.add(right);\n    return lst;\n  }\n\n  @Override\n  public Exprent copy() {\n    return new AssignmentExprent(left.copy(), right.copy(), bytecode);\n  }\n\n  @Override\n  public int getPrecedence() {\n    return 13;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    VarType leftType = left.getExprType();\n    VarType rightType = right.getExprType();\n\n    boolean fieldInClassInit = false, hiddenField = false;\n    if (left.type == Exprent.EXPRENT_FIELD) { // first assignment to a final field. Field name without \"this\" in front of it\n      FieldExprent field = (FieldExprent)left;\n      ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE));\n      if (node != null) {\n        StructField fd = node.classStruct.getField(field.getName(), field.getDescriptor().descriptorString);\n        if (fd != null) {\n          if (field.isStatic() && fd.hasModifier(CodeConstants.ACC_FINAL)) {\n            fieldInClassInit = true;\n          }\n          if (node.getWrapper() != null && node.getWrapper().getHiddenMembers().contains(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()))) {\n            hiddenField = true;\n          }\n        }\n      }\n    }\n\n    if (hiddenField) {\n      return new TextBuffer();\n    }\n\n    TextBuffer buffer = new TextBuffer();\n\n    if (fieldInClassInit) {\n      buffer.append(((FieldExprent)left).getName());\n    }\n    else {\n      buffer.append(left.toJava(indent, tracer));\n    }\n\n    if (right.type == EXPRENT_CONST) {\n      ((ConstExprent) right).adjustConstType(leftType);\n    }\n\n    TextBuffer res = right.toJava(indent, tracer);\n\n    if (condType == CONDITION_NONE &&\n        !leftType.isSuperset(rightType) &&\n        (rightType.equals(VarType.VARTYPE_OBJECT) || leftType.getType() != CodeConstants.TYPE_OBJECT)) {\n      if (right.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {\n        res.enclose(\"(\", \")\");\n      }\n\n      res.prepend(\"(\" + ExprProcessor.getCastTypeName(leftType, Collections.emptyList()) + \")\");\n    }\n\n    buffer.append(condType == CONDITION_NONE ? \" = \" : OPERATORS[condType]).append(res);\n\n    tracer.addMapping(bytecode);\n\n    return buffer;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == left) {\n      left = newExpr;\n    }\n    if (oldExpr == right) {\n      right = newExpr;\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof AssignmentExprent as)) return false;\n\n    return Objects.equals(left, as.getLeft()) &&\n           Objects.equals(right, as.getRight()) &&\n           condType == as.getCondType();\n  }\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public Exprent getLeft() {\n    return left;\n  }\n\n  public Exprent getRight() {\n    return right;\n  }\n\n  public void setRight(Exprent right) {\n    this.right = right;\n  }\n\n  public int getCondType() {\n    return condType;\n  }\n\n  public void setCondType(int condType) {\n    this.condType = condType;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.struct.StructMember;\nimport org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\nimport org.jetbrains.java.decompiler.util.TextUtil;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class ConstExprent extends Exprent {\n  private static final String SHORT_SIG = \"java/lang/Short\";\n  private static final String INT_SIG = \"java/lang/Integer\";\n  private static final String LONG_SIG = \"java/lang/Long\";\n  private static final String FLOAT_SIG = \"java/lang/Float\";\n  private static final String DOUBLE_SIG = \"java/lang/Double\";\n  private static final String MATH_SIG = \"java/lang/Math\";\n  private static final String MIN_VAL = \"MIN_VALUE\";\n  private static final String MAX_VAL = \"MAX_VALUE\";\n  private static final String POS_INF = \"POSITIVE_INFINITY\";\n  private static final String NEG_INF = \"NEGATIVE_INFINITY\";\n  private static final String NAN = \"NaN\";\n  private static final String MIN_NORM = \"MIN_NORMAL\";\n  private static final String E = \"E\";\n  private static final String PI = \"PI\";\n\n  @SuppressWarnings(\"UnnecessaryUnicodeEscape\")\n  private static final Map<Integer, String> CHAR_ESCAPES = Map.of(\n    0x8, \"\\\\b\",   /* \\u0008: backspace BS */\n    0x9, \"\\\\t\",   /* \\u0009: horizontal tab HT */\n    0xA, \"\\\\n\",   /* \\u000a: linefeed LF */\n    0xC, \"\\\\f\",   /* \\u000c: form feed FF */\n    0xD, \"\\\\r\",   /* \\u000d: carriage return CR */\n    0x27, \"\\\\'\",  /* \\u0027: single quote ' */\n    0x5C, \"\\\\\\\\\"  /* \\u005c: backslash \\ */\n  );\n\n  private StructMember parent;\n  private VarType constType;\n  private final Object value;\n  private final boolean boolPermitted;\n\n  public ConstExprent(int val, boolean boolPermitted, Set<Integer> bytecodeOffsets) {\n    this(guessType(val, boolPermitted), val, boolPermitted, bytecodeOffsets);\n  }\n\n  public ConstExprent(VarType constType, Object value, Set<Integer> bytecodeOffsets) {\n    this(constType, value, false, bytecodeOffsets);\n  }\n\n  public ConstExprent(VarType constType, Object value, Set<Integer> bytecodeOffsets, StructMember parent) {\n    this(constType, value, bytecodeOffsets);\n    this.parent = parent;\n  }\n\n  private ConstExprent(VarType constType, Object value, boolean boolPermitted, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_CONST);\n    this.constType = constType;\n    this.value = value;\n    this.boolPermitted = boolPermitted;\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  private static VarType guessType(int val, boolean boolPermitted) {\n    if (boolPermitted) {\n      VarType constType = VarType.VARTYPE_BOOLEAN;\n      if (val != 0 && val != 1) {\n        constType = constType.copy(true);\n      }\n      return constType;\n    }\n    else if (0 <= val && val <= 127) {\n      return VarType.VARTYPE_BYTECHAR;\n    }\n    else if (-128 <= val && val <= 127) {\n      return VarType.VARTYPE_BYTE;\n    }\n    else if (0 <= val && val <= 32767) {\n      return VarType.VARTYPE_SHORTCHAR;\n    }\n    else if (-32768 <= val && val <= 32767) {\n      return VarType.VARTYPE_SHORT;\n    }\n    else if (0 <= val && val <= 0xFFFF) {\n      return VarType.VARTYPE_CHAR;\n    }\n    else {\n      return VarType.VARTYPE_INT;\n    }\n  }\n\n  @Override\n  public Exprent copy() {\n    return new ConstExprent(constType, value, bytecode);\n  }\n\n  @Override\n  public VarType getExprType() {\n    return constType;\n  }\n\n  @Override\n  public int getExprentUse() {\n    return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    return new ArrayList<>();\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    boolean literal = DecompilerContext.getOption(IFernflowerPreferences.LITERALS_AS_IS);\n    boolean ascii = DecompilerContext.getOption(IFernflowerPreferences.ASCII_STRING_CHARACTERS);\n\n    tracer.addMapping(bytecode);\n\n    if (constType.getType() != CodeConstants.TYPE_NULL && value == null) {\n      return new TextBuffer(ExprProcessor.getCastTypeName(constType, Collections.emptyList()));\n    }\n\n    return switch (constType.getType()) {\n      case CodeConstants.TYPE_BOOLEAN -> new TextBuffer(Boolean.toString((Integer)value != 0));\n      case CodeConstants.TYPE_CHAR -> {\n        Integer val = (Integer)value;\n        String ret = CHAR_ESCAPES.get(val);\n        if (ret == null) {\n          char c = (char)val.intValue();\n          if (isPrintableAscii(c) || !ascii && TextUtil.isPrintableUnicode(c)) {\n            ret = String.valueOf(c);\n          }\n          else {\n            ret = TextUtil.charToUnicodeLiteral(c);\n          }\n        }\n        yield new TextBuffer(ret).enclose(\"'\", \"'\");\n      }\n      case CodeConstants.TYPE_BYTE -> new TextBuffer(value.toString());\n      case CodeConstants.TYPE_BYTECHAR, CodeConstants.TYPE_SHORT -> {\n        int shortVal = (Integer)value;\n        if (!literal) {\n          if (shortVal == Short.MAX_VALUE && !inConstantVariable(SHORT_SIG, MAX_VAL)) {\n            yield new FieldExprent(MAX_VAL, SHORT_SIG, true, null, FieldDescriptor.SHORT_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (shortVal == Short.MIN_VALUE && !inConstantVariable(SHORT_SIG, MIN_VAL)) {\n            yield new FieldExprent(MIN_VAL, SHORT_SIG, true, null, FieldDescriptor.SHORT_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n        }\n        yield new TextBuffer(value.toString());\n      }\n      case CodeConstants.TYPE_SHORTCHAR, CodeConstants.TYPE_INT -> {\n        int intVal = (Integer)value;\n        if (!literal) {\n          if (intVal == Integer.MAX_VALUE && !inConstantVariable(INT_SIG, MAX_VAL)) {\n            yield new FieldExprent(MAX_VAL, INT_SIG, true, null, FieldDescriptor.INTEGER_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (intVal == Integer.MIN_VALUE && !inConstantVariable(INT_SIG, MIN_VAL)) {\n            yield new FieldExprent(MIN_VAL, INT_SIG, true, null, FieldDescriptor.INTEGER_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n        }\n        yield new TextBuffer(value.toString());\n      }\n      case CodeConstants.TYPE_LONG -> {\n        long longVal = (Long)value;\n        if (!literal) {\n          if (longVal == Long.MAX_VALUE && !inConstantVariable(LONG_SIG, MAX_VAL)) {\n            yield new FieldExprent(MAX_VAL, LONG_SIG, true, null, FieldDescriptor.LONG_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (longVal == Long.MIN_VALUE && !inConstantVariable(LONG_SIG, MIN_VAL)) {\n            yield new FieldExprent(MIN_VAL, LONG_SIG, true, null, FieldDescriptor.LONG_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n        }\n        yield new TextBuffer(value.toString()).append('L');\n      }\n      case CodeConstants.TYPE_FLOAT -> {\n        float floatVal = (Float)value;\n        if (!literal) {\n          if (Float.isNaN(floatVal) && !inConstantVariable(FLOAT_SIG, NAN)) {\n            yield new FieldExprent(NAN, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (floatVal == Float.POSITIVE_INFINITY && !inConstantVariable(FLOAT_SIG, POS_INF)) {\n            yield new FieldExprent(POS_INF, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (floatVal == Float.NEGATIVE_INFINITY && !inConstantVariable(FLOAT_SIG, NEG_INF)) {\n            yield new FieldExprent(NEG_INF, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (floatVal == Float.MAX_VALUE && !inConstantVariable(FLOAT_SIG, MAX_VAL)) {\n            yield new FieldExprent(MAX_VAL, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (floatVal == Float.MIN_VALUE && !inConstantVariable(FLOAT_SIG, MIN_VAL)) {\n            yield new FieldExprent(MIN_VAL, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (floatVal == Float.MIN_NORMAL && !inConstantVariable(FLOAT_SIG, MIN_NORM)) {\n            yield new FieldExprent(MIN_NORM, FLOAT_SIG, true, null, FieldDescriptor.FLOAT_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n        }\n        else if (Float.isNaN(floatVal)) {\n          yield new TextBuffer(\"0.0F / 0.0F\");\n        }\n        else if (floatVal == Float.POSITIVE_INFINITY) {\n          yield new TextBuffer(\"1.0F / 0.0F\");\n        }\n        else if (floatVal == Float.NEGATIVE_INFINITY) {\n          yield new TextBuffer(\"-1.0F / 0.0F\");\n        }\n        yield new TextBuffer(value.toString()).append('F');\n      }\n      case CodeConstants.TYPE_DOUBLE -> {\n        double doubleVal = (Double)value;\n        if (!literal) {\n          if (Double.isNaN(doubleVal) && !inConstantVariable(DOUBLE_SIG, NAN)) {\n            yield new FieldExprent(NAN, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (doubleVal == Double.POSITIVE_INFINITY && !inConstantVariable(DOUBLE_SIG, POS_INF)) {\n            yield new FieldExprent(POS_INF, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (doubleVal == Double.NEGATIVE_INFINITY && !inConstantVariable(DOUBLE_SIG, NEG_INF)) {\n            yield new FieldExprent(NEG_INF, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (doubleVal == Double.MAX_VALUE && !inConstantVariable(DOUBLE_SIG, MAX_VAL)) {\n            yield new FieldExprent(MAX_VAL, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (doubleVal == Double.MIN_VALUE && !inConstantVariable(DOUBLE_SIG, MIN_VAL)) {\n            yield new FieldExprent(MIN_VAL, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (doubleVal == Double.MIN_NORMAL && !inConstantVariable(DOUBLE_SIG, MIN_NORM)) {\n            yield new FieldExprent(MIN_NORM, DOUBLE_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (doubleVal == Math.E && !inConstantVariable(MATH_SIG, E)) {\n            yield new FieldExprent(E, MATH_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n          else if (doubleVal == Math.PI && !inConstantVariable(MATH_SIG, PI)) {\n            yield new FieldExprent(PI, MATH_SIG, true, null, FieldDescriptor.DOUBLE_DESCRIPTOR, bytecode).toJava(0, tracer);\n          }\n        }\n        else if (Double.isNaN(doubleVal)) {\n          yield new TextBuffer(\"0.0 / 0.0\");\n        }\n        else if (doubleVal == Double.POSITIVE_INFINITY) {\n          yield new TextBuffer(\"1.0 / 0.0\");\n        }\n        else if (doubleVal == Double.NEGATIVE_INFINITY) {\n          yield new TextBuffer(\"-1.0 / 0.0\");\n        }\n        yield new TextBuffer(value.toString());\n      }\n      case CodeConstants.TYPE_NULL -> new TextBuffer(\"null\");\n      case CodeConstants.TYPE_OBJECT -> {\n        if (constType.equals(VarType.VARTYPE_STRING)) {\n          yield new TextBuffer(convertStringToJava(value.toString(), ascii)).enclose(\"\\\"\", \"\\\"\");\n        }\n        else if (constType.equals(VarType.VARTYPE_CLASS)) {\n          String stringVal = value.toString();\n          VarType type = new VarType(stringVal, !stringVal.startsWith(\"[\"));\n          yield new TextBuffer(ExprProcessor.getCastTypeName(type, Collections.emptyList())).append(\".class\");\n        }\n        throw new RuntimeException(\"invalid constant type: \" + constType);\n      }\n      default -> throw new RuntimeException(\"invalid constant type: \" + constType);\n    };\n  }\n\n  private boolean inConstantVariable(String classSignature, String variableName) {\n    ClassesProcessor.ClassNode node = (ClassesProcessor.ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);\n    return node.classStruct.qualifiedName.equals(classSignature) &&\n           parent instanceof StructField &&\n           ((StructField)parent).getName().equals(variableName);\n  }\n\n  public boolean isNull() {\n    return CodeConstants.TYPE_NULL == constType.getType();\n  }\n\n  private static String convertStringToJava(String value, boolean ascii) {\n    char[] arr = value.toCharArray();\n    StringBuilder buffer = new StringBuilder(arr.length);\n\n    for (char c : arr) {\n      switch (c) {\n        case '\\\\' -> //  u005c: backslash \\\n          buffer.append(\"\\\\\\\\\");\n        case 0x8 -> // \"\\\\\\\\b\");  //  u0008: backspace BS\n          buffer.append(\"\\\\b\");\n        case 0x9 -> //\"\\\\\\\\t\");  //  u0009: horizontal tab HT\n          buffer.append(\"\\\\t\");\n        case 0xA -> //\"\\\\\\\\n\");  //  u000a: linefeed LF\n          buffer.append(\"\\\\n\");\n        case 0xC -> //\"\\\\\\\\f\");  //  u000c: form feed FF\n          buffer.append(\"\\\\f\");\n        case 0xD -> //\"\\\\\\\\r\");  //  u000d: carriage return CR\n          buffer.append(\"\\\\r\");\n        case 0x22 -> //\"\\\\\\\\\\\"\"); // u0022: double quote \"\n          buffer.append(\"\\\\\\\"\");\n\n        default -> {\n          if (isPrintableAscii(c) || !ascii && TextUtil.isPrintableUnicode(c)) {\n            buffer.append(c);\n          }\n          else {\n            buffer.append(TextUtil.charToUnicodeLiteral(c));\n          }\n        }\n      }\n    }\n\n    return buffer.toString();\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof ConstExprent cn)) return false;\n\n    return Objects.equals(constType, cn.getConstType()) &&\n           Objects.equals(value, cn.getValue());\n  }\n\n  @Override\n  public int hashCode() {\n    int result = constType != null ? constType.hashCode() : 0;\n    result = 31 * result + (value != null ? value.hashCode() : 0);\n    return result;\n  }\n\n  public boolean hasBooleanValue() {\n    switch (constType.getType()) {\n      case CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_BYTE, CodeConstants.TYPE_BYTECHAR,\n        CodeConstants.TYPE_SHORT, CodeConstants.TYPE_SHORTCHAR, CodeConstants.TYPE_INT -> {\n        int value = (Integer)this.value;\n        return value == 0 || (DecompilerContext.getOption(IFernflowerPreferences.BOOLEAN_TRUE_ONE) && value == 1);\n      }\n    }\n\n    return false;\n  }\n\n  public boolean hasValueOne() {\n    return switch (constType.getType()) {\n      case CodeConstants.TYPE_BOOLEAN, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_BYTE, CodeConstants.TYPE_BYTECHAR,\n        CodeConstants.TYPE_SHORT, CodeConstants.TYPE_SHORTCHAR, CodeConstants.TYPE_INT ->\n        (Integer)value == 1;\n      case CodeConstants.TYPE_LONG -> ((Long)value).intValue() == 1;\n      case CodeConstants.TYPE_DOUBLE -> ((Double)value).intValue() == 1;\n      case CodeConstants.TYPE_FLOAT -> ((Float)value).intValue() == 1;\n      default -> false;\n    };\n  }\n\n  public static ConstExprent getZeroConstant(int type) {\n    return switch (type) {\n      case CodeConstants.TYPE_INT -> new ConstExprent(VarType.VARTYPE_INT, 0, null);\n      case CodeConstants.TYPE_LONG -> new ConstExprent(VarType.VARTYPE_LONG, 0L, null);\n      case CodeConstants.TYPE_DOUBLE -> new ConstExprent(VarType.VARTYPE_DOUBLE, 0d, null);\n      case CodeConstants.TYPE_FLOAT -> new ConstExprent(VarType.VARTYPE_FLOAT, 0f, null);\n      default -> throw new RuntimeException(\"Invalid argument: \" + type);\n    };\n  }\n\n  public VarType getConstType() {\n    return constType;\n  }\n\n  public void setConstType(VarType constType) {\n    this.constType = constType;\n  }\n\n  public void adjustConstType(VarType expectedType) {\n    // BYTECHAR and SHORTCHAR => CHAR in the CHAR context\n    if ((expectedType.equals(VarType.VARTYPE_CHAR) || expectedType.equals(VarType.VARTYPE_CHARACTER)) &&\n            (constType.equals(VarType.VARTYPE_BYTECHAR) || constType.equals(VarType.VARTYPE_SHORTCHAR))) {\n      int intValue = getIntValue();\n      if (isPrintableAscii(intValue) || CHAR_ESCAPES.containsKey(intValue)) {\n        setConstType(VarType.VARTYPE_CHAR);\n      }\n    }\n    // BYTE, BYTECHAR, SHORTCHAR, SHORT, CHAR => INT in the INT context\n    else if ((expectedType.equals(VarType.VARTYPE_INT) || expectedType.equals(VarType.VARTYPE_INTEGER)) &&\n             constType.getTypeFamily() == CodeConstants.TYPE_FAMILY_INTEGER) {\n      setConstType(VarType.VARTYPE_INT);\n    }\n  }\n\n  private static boolean isPrintableAscii(int c) {\n    return c >= 32 && c < 127;\n  }\n\n\n  public Object getValue() {\n    return value;\n  }\n\n  public int getIntValue() {\n    return (Integer)value;\n  }\n\n  public boolean isBoolPermitted() {\n    return boolPermitted;\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (!super.match(matchNode, engine)) {\n      return false;\n    }\n\n    for (Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {\n      RuleValue value = rule.getValue();\n      MatchProperties key = rule.getKey();\n\n      if (key == MatchProperties.EXPRENT_CONSTTYPE) {\n        if (!value.value.equals(this.constType)) {\n          return false;\n        }\n      }\n      else if (key == MatchProperties.EXPRENT_CONSTVALUE) {\n        if (value.isVariable() && !engine.checkAndSetVariableValue(value.value.toString(), this.value)) {\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;\nimport org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\npublic class ExitExprent extends Exprent {\n\n  public static final int EXIT_RETURN = 0;\n  public static final int EXIT_THROW = 1;\n\n  private final int exitType;\n  private Exprent value;\n  private final VarType retType;\n\n  public ExitExprent(int exitType, Exprent value, VarType retType, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_EXIT);\n    this.exitType = exitType;\n    this.value = value;\n    this.retType = retType;\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  @Override\n  public Exprent copy() {\n    return new ExitExprent(exitType, value == null ? null : value.copy(), retType, bytecode);\n  }\n\n  @Override\n  public CheckTypesResult checkExprTypeBounds() {\n    CheckTypesResult result = new CheckTypesResult();\n\n    if (exitType == EXIT_RETURN && retType.getType() != CodeConstants.TYPE_VOID) {\n      result.addMinTypeExprent(value, VarType.getMinTypeInFamily(retType.getTypeFamily()));\n      result.addMaxTypeExprent(value, retType);\n    }\n\n    return result;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n    if (value != null) {\n      lst.add(value);\n    }\n    return lst;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    tracer.addMapping(bytecode);\n\n    if (exitType == EXIT_RETURN) {\n      TextBuffer buffer = new TextBuffer(\"return\");\n\n      if (retType.getType() != CodeConstants.TYPE_VOID) {\n        buffer.append(' ');\n        ExprProcessor.getCastedExprent(value, retType, buffer, indent, false, tracer);\n      }\n\n      return buffer;\n    }\n    else {\n      MethodWrapper method = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);\n      ClassNode node = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE));\n\n      if (method != null && node != null) {\n        StructExceptionsAttribute attr = method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);\n\n        if (attr != null) {\n          String classname = null;\n\n          for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {\n            String exClassName = attr.getExcClassname(i, node.classStruct.getPool());\n            if (\"java/lang/Throwable\".equals(exClassName)) {\n              classname = exClassName;\n              break;\n            }\n            else if (\"java/lang/Exception\".equals(exClassName)) {\n              classname = exClassName;\n            }\n          }\n\n          if (classname != null) {\n            VarType exType = new VarType(classname, true);\n            TextBuffer buffer = new TextBuffer(\"throw \");\n            ExprProcessor.getCastedExprent(value, exType, buffer, indent, false, tracer);\n            return buffer;\n          }\n        }\n      }\n\n      return value.toJava(indent, tracer).prepend(\"throw \");\n    }\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == value) {\n      value = newExpr;\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof ExitExprent et)) return false;\n\n    return exitType == et.getExitType() &&\n           Objects.equals(value, et.getValue());\n  }\n\n  public int getExitType() {\n    return exitType;\n  }\n\n  public Exprent getValue() {\n    return value;\n  }\n\n  public VarType getRetType() {\n    return retType;\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (!super.match(matchNode, engine)) {\n      return false;\n    }\n\n    Integer type = (Integer)matchNode.getRuleValue(MatchProperties.EXPRENT_EXITTYPE);\n    return type == null || this.exitType == type;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExprUtil.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic final class ExprUtil {\n  public static List<VarVersionPair> getSyntheticParametersMask(String className, String descriptor, int parameters) {\n    ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(className);\n    return node != null ? getSyntheticParametersMask(node, descriptor, parameters) : null;\n  }\n\n  public static List<VarVersionPair> getSyntheticParametersMask(ClassNode node, String descriptor, int parameters) {\n    List<VarVersionPair> mask = null;\n\n    ClassWrapper wrapper = node.getWrapper();\n    if (wrapper != null) {\n      // own class\n      MethodWrapper methodWrapper = wrapper.getMethodWrapper(CodeConstants.INIT_NAME, descriptor);\n      if (methodWrapper == null) {\n        if (DecompilerContext.getOption(IFernflowerPreferences.IGNORE_INVALID_BYTECODE)) {\n          return null;\n        }\n        throw new RuntimeException(\"Constructor \" + node.classStruct.qualifiedName + \".\" + CodeConstants.INIT_NAME + descriptor + \" not found\");\n      }\n      mask = methodWrapper.synthParameters;\n    }\n    else if (parameters > 0 && node.type == ClassNode.CLASS_MEMBER && (node.access & CodeConstants.ACC_STATIC) == 0) {\n      // non-static member class\n      mask = new ArrayList<>(Collections.nCopies(parameters, null));\n      mask.set(0, new VarVersionPair(-1, 0));\n    }\n\n    return mask;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java",
    "content": "/*\n * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\n */\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.match.IMatchable;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\npublic class Exprent implements IMatchable {\n  public static final int MULTIPLE_USES = 1;\n  public static final int SIDE_EFFECTS_FREE = 2;\n  public static final int BOTH_FLAGS = 3;\n\n  public static final int EXPRENT_ARRAY = 1;\n  public static final int EXPRENT_ASSIGNMENT = 2;\n  public static final int EXPRENT_CONST = 3;\n  public static final int EXPRENT_EXIT = 4;\n  public static final int EXPRENT_FIELD = 5;\n  public static final int EXPRENT_FUNCTION = 6;\n  public static final int EXPRENT_IF = 7;\n  public static final int EXPRENT_INVOCATION = 8;\n  public static final int EXPRENT_MONITOR = 9;\n  public static final int EXPRENT_NEW = 10;\n  public static final int EXPRENT_SWITCH = 11;\n  public static final int EXPRENT_VAR = 12;\n  public static final int EXPRENT_ANNOTATION = 13;\n  public static final int EXPRENT_ASSERT = 14;\n\n  public final int type;\n  public final int id;\n  public Set<Integer> bytecode = null;  // offsets of bytecode instructions decompiled to this exprent\n\n  public Exprent(int type) {\n    this.type = type;\n    this.id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.EXPRESSION_COUNTER);\n  }\n\n  public int getPrecedence() {\n    return 0; // the highest precedence\n  }\n\n  public VarType getExprType() {\n    return VarType.VARTYPE_VOID;\n  }\n\n  public int getExprentUse() {\n    return 0;\n  }\n\n  public CheckTypesResult checkExprTypeBounds() {\n    return null;\n  }\n\n  public boolean containsExprent(Exprent exprent) {\n    if (equals(exprent)) {\n      return true;\n    }\n    List<Exprent> lst = getAllExprents();\n    for (int i = lst.size() - 1; i >= 0; i--) {\n      if (lst.get(i).containsExprent(exprent)) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  public List<Exprent> getAllExprents(boolean recursive) {\n    List<Exprent> lst = getAllExprents();\n    if (recursive) {\n      for (int i = lst.size() - 1; i >= 0; i--) {\n        lst.addAll(lst.get(i).getAllExprents(true));\n      }\n    }\n    return lst;\n  }\n\n  public Set<VarVersionPair> getAllVariables() {\n    List<Exprent> lstAllExprents = getAllExprents(true);\n    lstAllExprents.add(this);\n\n    Set<VarVersionPair> set = new HashSet<>();\n    for (Exprent expr : lstAllExprents) {\n      if (expr.type == EXPRENT_VAR) {\n        set.add(new VarVersionPair((VarExprent)expr));\n      }\n    }\n    return set;\n  }\n\n  public List<Exprent> getAllExprents() {\n    throw new RuntimeException(\"not implemented\");\n  }\n\n  public Exprent copy() {\n    throw new RuntimeException(\"not implemented\");\n  }\n\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    throw new RuntimeException(\"not implemented\");\n  }\n\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) { }\n\n  public void addBytecodeOffsets(Collection<Integer> bytecodeOffsets) {\n    if (bytecodeOffsets != null && !bytecodeOffsets.isEmpty()) {\n      if (bytecode == null) {\n        bytecode = new HashSet<>(bytecodeOffsets);\n      }\n      else {\n        bytecode.addAll(bytecodeOffsets);\n      }\n    }\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public IMatchable findObject(MatchNode matchNode, int index) {\n    if (matchNode.getType() != MatchNode.MATCHNODE_EXPRENT) {\n      return null;\n    }\n\n    List<Exprent> lstAllExprents = getAllExprents();\n    if (lstAllExprents == null || lstAllExprents.isEmpty()) {\n      return null;\n    }\n\n    String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);\n    if (position != null) {\n      if (position.matches(\"-?\\\\d+\")) {\n        return lstAllExprents.get((lstAllExprents.size() + Integer.parseInt(position)) % lstAllExprents.size()); // care for negative positions\n      }\n    }\n    else if (index < lstAllExprents.size()) { // use 'index' parameter\n      return lstAllExprents.get(index);\n    }\n\n    return null;\n  }\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (matchNode.getType() != MatchNode.MATCHNODE_EXPRENT) {\n      return false;\n    }\n\n    for (Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {\n      MatchProperties key = rule.getKey();\n      if (key == MatchProperties.EXPRENT_TYPE && this.type != (Integer)rule.getValue().value) {\n        return false;\n      }\n      if (key == MatchProperties.EXPRENT_RET && !engine.checkAndSetVariableValue((String)rule.getValue().value, this)) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    return toJava(0, BytecodeMappingTracer.DUMMY).toString();\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.LinkConstant;\nimport org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\nimport org.jetbrains.java.decompiler.util.TextUtil;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\npublic class FieldExprent extends Exprent {\n  private final String name;\n  private final String classname;\n  private final boolean isStatic;\n  private Exprent instance;\n  private final FieldDescriptor descriptor;\n\n  public FieldExprent(LinkConstant cn, Exprent instance, Set<Integer> bytecodeOffsets) {\n    this(cn.elementName, cn.className, instance == null, instance, FieldDescriptor.parseDescriptor(cn.descriptor), bytecodeOffsets);\n  }\n\n  public FieldExprent(String name, String classname, boolean isStatic, Exprent instance, FieldDescriptor descriptor, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_FIELD);\n    this.name = name;\n    this.classname = classname;\n    this.isStatic = isStatic;\n    this.instance = instance;\n    this.descriptor = descriptor;\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  @Override\n  public VarType getExprType() {\n    return descriptor.type;\n  }\n\n  @Override\n  public int getExprentUse() {\n    return 0; // multiple references to a field considered dangerous in a multithreaded environment, thus no Exprent.MULTIPLE_USES set here\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n    if (instance != null) {\n      lst.add(instance);\n    }\n    return lst;\n  }\n\n  @Override\n  public Exprent copy() {\n    return new FieldExprent(name, classname, isStatic, instance == null ? null : instance.copy(), descriptor, bytecode);\n  }\n\n  private boolean isAmbiguous() {\n    MethodWrapper method = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);\n    if (method != null) {\n      StructLocalVariableTableAttribute attr = method.methodStruct.getLocalVariableAttr();\n      if (attr != null) {\n        return attr.containsName(name);\n      }\n    }\n\n    return false;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n\n    if (isStatic) {\n      ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);\n      if (node == null || !classname.equals(node.classStruct.qualifiedName) || isAmbiguous()) {\n        buf.append(DecompilerContext.getImportCollector().getNestedNameInClassContext(ExprProcessor.buildJavaClassName(classname)));\n        buf.append(\".\");\n      }\n    }\n    else {\n      String super_qualifier = null;\n\n      if (instance != null && instance.type == Exprent.EXPRENT_VAR) {\n        VarExprent instVar = (VarExprent)instance;\n        VarVersionPair pair = new VarVersionPair(instVar);\n\n        MethodWrapper currentMethod = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);\n\n        if (currentMethod != null) { // FIXME: remove\n          String this_classname = currentMethod.varproc.getThisVars().get(pair);\n\n          if (this_classname != null) {\n            if (!classname.equals(this_classname)) { // TODO: direct comparison to the super class?\n              super_qualifier = this_classname;\n            }\n          }\n        }\n      }\n\n      if (super_qualifier != null) {\n        TextUtil.writeQualifiedSuper(buf, super_qualifier);\n      }\n      else {\n        TextBuffer buff = new TextBuffer();\n        boolean casted = ExprProcessor.getCastedExprent(instance, new VarType(CodeConstants.TYPE_OBJECT, 0, classname), buff, indent, true, tracer);\n        String res = buff.toString();\n\n        if (casted || instance.getPrecedence() > getPrecedence()) {\n          res = \"(\" + res + \")\";\n        }\n\n        buf.append(res);\n      }\n\n      if (buf.toString().equals(\n        VarExprent.VAR_NAMELESS_ENCLOSURE)) { // FIXME: workaround for field access of an anonymous enclosing class. Find a better way.\n        buf.setLength(0);\n      }\n      else {\n        buf.append(\".\");\n      }\n    }\n\n    buf.append(name);\n\n    tracer.addMapping(bytecode);\n\n    return buf;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == instance) {\n      instance = newExpr;\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof FieldExprent ft)) return false;\n\n    return Objects.equals(name, ft.getName()) &&\n           Objects.equals(classname, ft.getClassname()) &&\n           isStatic == ft.isStatic() &&\n           Objects.equals(instance, ft.getInstance()) &&\n           Objects.equals(descriptor, ft.getDescriptor());\n  }\n\n  public String getClassname() {\n    return classname;\n  }\n\n  public FieldDescriptor getDescriptor() {\n    return descriptor;\n  }\n\n  public Exprent getInstance() {\n    return instance;\n  }\n\n  public boolean isStatic() {\n    return isStatic;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (!super.match(matchNode, engine)) {\n      return false;\n    }\n\n    RuleValue rule = matchNode.getRules().get(MatchProperties.EXPRENT_FIELD_NAME);\n    if (rule != null) {\n      if (rule.isVariable()) {\n        return engine.checkAndSetVariableValue((String)rule.value, this.name);\n      }\n      else {\n        return rule.value.equals(this.name);\n      }\n    }\n\n    return true;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.util.ListStack;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\n\npublic class FunctionExprent extends Exprent {\n\n  public static final int FUNCTION_ADD = 0;\n  public static final int FUNCTION_SUB = 1;\n  public static final int FUNCTION_MUL = 2;\n  public static final int FUNCTION_DIV = 3;\n\n  public static final int FUNCTION_AND = 4;\n  public static final int FUNCTION_OR = 5;\n  public static final int FUNCTION_XOR = 6;\n\n  public static final int FUNCTION_REM = 7;\n\n  public static final int FUNCTION_SHL = 8;\n  public static final int FUNCTION_SHR = 9;\n  public static final int FUNCTION_USHR = 10;\n\n  public static final int FUNCTION_BIT_NOT = 11;\n  public static final int FUNCTION_BOOL_NOT = 12;\n  public static final int FUNCTION_NEG = 13;\n\n  public final static int FUNCTION_I2L = 14;\n  public final static int FUNCTION_I2F = 15;\n  public final static int FUNCTION_I2D = 16;\n  public final static int FUNCTION_L2I = 17;\n  public final static int FUNCTION_L2F = 18;\n  public final static int FUNCTION_L2D = 19;\n  public final static int FUNCTION_F2I = 20;\n  public final static int FUNCTION_F2L = 21;\n  public final static int FUNCTION_F2D = 22;\n  public final static int FUNCTION_D2I = 23;\n  public final static int FUNCTION_D2L = 24;\n  public final static int FUNCTION_D2F = 25;\n  public final static int FUNCTION_I2B = 26;\n  public final static int FUNCTION_I2C = 27;\n  public final static int FUNCTION_I2S = 28;\n\n  public final static int FUNCTION_CAST = 29;\n  public final static int FUNCTION_INSTANCEOF = 30;\n\n  public final static int FUNCTION_ARRAY_LENGTH = 31;\n\n  public final static int FUNCTION_IMM = 32;\n  public final static int FUNCTION_MMI = 33;\n\n  public final static int FUNCTION_IPP = 34;\n  public final static int FUNCTION_PPI = 35;\n\n  public final static int FUNCTION_IIF = 36;\n\n  public final static int FUNCTION_LCMP = 37;\n  public final static int FUNCTION_FCMPL = 38;\n  public final static int FUNCTION_FCMPG = 39;\n  public final static int FUNCTION_DCMPL = 40;\n  public final static int FUNCTION_DCMPG = 41;\n\n  public static final int FUNCTION_EQ = 42;\n  public static final int FUNCTION_NE = 43;\n  public static final int FUNCTION_LT = 44;\n  public static final int FUNCTION_GE = 45;\n  public static final int FUNCTION_GT = 46;\n  public static final int FUNCTION_LE = 47;\n\n  public static final int FUNCTION_CADD = 48;\n  public static final int FUNCTION_COR = 49;\n\n  public static final int FUNCTION_STR_CONCAT = 50;\n\n  private static final VarType[] TYPES = {\n    VarType.VARTYPE_LONG,\n    VarType.VARTYPE_FLOAT,\n    VarType.VARTYPE_DOUBLE,\n    VarType.VARTYPE_INT,\n    VarType.VARTYPE_FLOAT,\n    VarType.VARTYPE_DOUBLE,\n    VarType.VARTYPE_INT,\n    VarType.VARTYPE_LONG,\n    VarType.VARTYPE_DOUBLE,\n    VarType.VARTYPE_INT,\n    VarType.VARTYPE_LONG,\n    VarType.VARTYPE_FLOAT,\n    VarType.VARTYPE_BYTE,\n    VarType.VARTYPE_CHAR,\n    VarType.VARTYPE_SHORT\n  };\n\n  private static final String[] OPERATORS = {\n    \" + \",\n    \" - \",\n    \" * \",\n    \" / \",\n    \" & \",\n    \" | \",\n    \" ^ \",\n    \" % \",\n    \" << \",\n    \" >> \",\n    \" >>> \",\n    \" == \",\n    \" != \",\n    \" < \",\n    \" >= \",\n    \" > \",\n    \" <= \",\n    \" && \",\n    \" || \",\n    \" + \"\n  };\n\n  private static final int[] PRECEDENCE = {\n    3,   // FUNCTION_ADD\n    3,   // FUNCTION_SUB\n    2,   // FUNCTION_MUL\n    2,   // FUNCTION_DIV\n    7,   // FUNCTION_AND\n    9,   // FUNCTION_OR\n    8,   // FUNCTION_XOR\n    2,   // FUNCTION_REM\n    4,   // FUNCTION_SHL\n    4,   // FUNCTION_SHR\n    4,   // FUNCTION_USHR\n    1,   // FUNCTION_BIT_NOT\n    1,   // FUNCTION_BOOL_NOT\n    1,   // FUNCTION_NEG\n    1,   // FUNCTION_I2L\n    1,   // FUNCTION_I2F\n    1,   // FUNCTION_I2D\n    1,   // FUNCTION_L2I\n    1,   // FUNCTION_L2F\n    1,   // FUNCTION_L2D\n    1,   // FUNCTION_F2I\n    1,   // FUNCTION_F2L\n    1,   // FUNCTION_F2D\n    1,   // FUNCTION_D2I\n    1,   // FUNCTION_D2L\n    1,   // FUNCTION_D2F\n    1,   // FUNCTION_I2B\n    1,   // FUNCTION_I2C\n    1,   // FUNCTION_I2S\n    1,   // FUNCTION_CAST\n    6,   // FUNCTION_INSTANCEOF\n    0,   // FUNCTION_ARRAY_LENGTH\n    1,   // FUNCTION_IMM\n    1,   // FUNCTION_MMI\n    1,   // FUNCTION_IPP\n    1,   // FUNCTION_PPI\n    12,  // FUNCTION_IFF\n    -1,  // FUNCTION_LCMP\n    -1,  // FUNCTION_FCMPL\n    -1,  // FUNCTION_FCMPG\n    -1,  // FUNCTION_DCMPL\n    -1,  // FUNCTION_DCMPG\n    6,   // FUNCTION_EQ = 41;\n    6,   // FUNCTION_NE = 42;\n    5,   // FUNCTION_LT = 43;\n    5,   // FUNCTION_GE = 44;\n    5,   // FUNCTION_GT = 45;\n    5,   // FUNCTION_LE = 46;\n    10,  // FUNCTION_CADD = 47;\n    11,  // FUNCTION_COR = 48;\n    3    // FUNCTION_STR_CONCAT = 49;\n  };\n\n  private static final Set<Integer> ASSOCIATIVITY = new HashSet<>(Arrays.asList(\n    FUNCTION_ADD, FUNCTION_MUL, FUNCTION_AND, FUNCTION_OR, FUNCTION_XOR, FUNCTION_CADD, FUNCTION_COR, FUNCTION_STR_CONCAT));\n\n  private int funcType;\n  private VarType implicitType;\n  private final List<Exprent> lstOperands;\n\n  public FunctionExprent(int funcType, ListStack<Exprent> stack, Set<Integer> bytecodeOffsets) {\n    this(funcType, new ArrayList<>(), bytecodeOffsets);\n\n    if (funcType >= FUNCTION_BIT_NOT && funcType <= FUNCTION_PPI && funcType != FUNCTION_CAST && funcType != FUNCTION_INSTANCEOF) {\n      lstOperands.add(stack.pop());\n    }\n    else if (funcType == FUNCTION_IIF) {\n      throw new RuntimeException(\"no direct instantiation possible\");\n    }\n    else {\n      Exprent expr = stack.pop();\n      lstOperands.add(stack.pop());\n      lstOperands.add(expr);\n    }\n  }\n\n  public FunctionExprent(int funcType, List<Exprent> operands, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_FUNCTION);\n    this.funcType = funcType;\n    this.lstOperands = operands;\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  public FunctionExprent(int funcType, Exprent operand, Set<Integer> bytecodeOffsets) {\n    this(funcType, new ArrayList<>(1), bytecodeOffsets);\n    lstOperands.add(operand);\n  }\n\n  @Override\n  public VarType getExprType() {\n    VarType exprType = null;\n\n    if (funcType <= FUNCTION_NEG || funcType == FUNCTION_IPP || funcType == FUNCTION_PPI || funcType == FUNCTION_IMM || funcType == FUNCTION_MMI) {\n      VarType type1 = lstOperands.get(0).getExprType();\n      VarType type2 = null;\n      if (lstOperands.size() > 1) {\n        type2 = lstOperands.get(1).getExprType();\n      }\n\n      switch (funcType) {\n        case FUNCTION_IMM, FUNCTION_MMI, FUNCTION_IPP, FUNCTION_PPI -> exprType = implicitType;\n        case FUNCTION_BOOL_NOT -> exprType = VarType.VARTYPE_BOOLEAN;\n        case FUNCTION_SHL, FUNCTION_SHR, FUNCTION_USHR, FUNCTION_BIT_NOT, FUNCTION_NEG -> exprType = getMaxVarType(new VarType[]{type1});\n        case FUNCTION_ADD, FUNCTION_SUB, FUNCTION_MUL, FUNCTION_DIV, FUNCTION_REM -> exprType = getMaxVarType(new VarType[]{type1, type2});\n        case FUNCTION_AND, FUNCTION_OR, FUNCTION_XOR -> {\n          if (type1.getType() == CodeConstants.TYPE_BOOLEAN & type2.getType() == CodeConstants.TYPE_BOOLEAN) {\n            exprType = VarType.VARTYPE_BOOLEAN;\n          }\n          else {\n            exprType = getMaxVarType(new VarType[]{type1, type2});\n          }\n        }\n      }\n    }\n    else if (funcType == FUNCTION_CAST) {\n      exprType = lstOperands.get(1).getExprType();\n    }\n    else if (funcType == FUNCTION_IIF) {\n      Exprent param1 = lstOperands.get(1);\n      Exprent param2 = lstOperands.get(2);\n      VarType supertype = VarType.getCommonSupertype(param1.getExprType(), param2.getExprType());\n      if (param1.type == Exprent.EXPRENT_CONST && param2.type == Exprent.EXPRENT_CONST &&\n          supertype.getType() != CodeConstants.TYPE_BOOLEAN && VarType.VARTYPE_INT.isSuperset(supertype)) {\n        exprType = VarType.VARTYPE_INT;\n      }\n      else {\n        exprType = supertype;\n      }\n    }\n    else if (funcType == FUNCTION_STR_CONCAT) {\n      exprType = VarType.VARTYPE_STRING;\n    }\n    else if (funcType >= FUNCTION_EQ || funcType == FUNCTION_INSTANCEOF) {\n      exprType = VarType.VARTYPE_BOOLEAN;\n    }\n    else if (funcType >= FUNCTION_ARRAY_LENGTH) {\n      exprType = VarType.VARTYPE_INT;\n    }\n    else {\n      exprType = TYPES[funcType - FUNCTION_I2L];\n    }\n\n    return exprType;\n  }\n\n  @Override\n  public int getExprentUse() {\n    if (funcType >= FUNCTION_IMM && funcType <= FUNCTION_PPI) {\n      return 0;\n    }\n    else {\n      int ret = Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE;\n      for (Exprent expr : lstOperands) {\n        ret &= expr.getExprentUse();\n      }\n      return ret;\n    }\n  }\n\n  @Override\n  public CheckTypesResult checkExprTypeBounds() {\n    CheckTypesResult result = new CheckTypesResult();\n\n    Exprent param1 = lstOperands.get(0);\n    VarType type1 = param1.getExprType();\n    Exprent param2 = null;\n    VarType type2 = null;\n\n    if (lstOperands.size() > 1) {\n      param2 = lstOperands.get(1);\n      type2 = param2.getExprType();\n    }\n\n    switch (funcType) {\n      case FUNCTION_IIF:\n        VarType supertype = getExprType();\n        result.addMinTypeExprent(param1, VarType.VARTYPE_BOOLEAN);\n        result.addMinTypeExprent(param2, VarType.getMinTypeInFamily(supertype.getTypeFamily()));\n        result.addMinTypeExprent(lstOperands.get(2), VarType.getMinTypeInFamily(supertype.getTypeFamily()));\n        break;\n      case FUNCTION_I2L:\n      case FUNCTION_I2F:\n      case FUNCTION_I2D:\n      case FUNCTION_I2B:\n      case FUNCTION_I2C:\n      case FUNCTION_I2S:\n        result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);\n        result.addMaxTypeExprent(param1, VarType.VARTYPE_INT);\n        break;\n      case FUNCTION_IMM:\n      case FUNCTION_IPP:\n      case FUNCTION_MMI:\n      case FUNCTION_PPI:\n        result.addMinTypeExprent(param1, implicitType);\n        result.addMaxTypeExprent(param1, implicitType);\n        break;\n      case FUNCTION_ADD:\n      case FUNCTION_SUB:\n      case FUNCTION_MUL:\n      case FUNCTION_DIV:\n      case FUNCTION_REM:\n      case FUNCTION_SHL:\n      case FUNCTION_SHR:\n      case FUNCTION_USHR:\n      case FUNCTION_LT:\n      case FUNCTION_GE:\n      case FUNCTION_GT:\n      case FUNCTION_LE:\n        result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR);\n      case FUNCTION_BIT_NOT:\n        // case FUNCTION_BOOL_NOT:\n      case FUNCTION_NEG:\n        result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);\n        break;\n      case FUNCTION_AND:\n      case FUNCTION_OR:\n      case FUNCTION_XOR:\n      case FUNCTION_EQ:\n      case FUNCTION_NE: {\n        if (type1.getType() == CodeConstants.TYPE_BOOLEAN) {\n          if (type2.isStrictSuperset(type1)) {\n            result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);\n          }\n          else { // both are booleans\n            boolean param1_false_boolean =\n              type1.isFalseBoolean() || (param1.type == Exprent.EXPRENT_CONST && !((ConstExprent)param1).hasBooleanValue());\n            boolean param2_false_boolean =\n              type1.isFalseBoolean() || (param2.type == Exprent.EXPRENT_CONST && !((ConstExprent)param2).hasBooleanValue());\n\n            if (param1_false_boolean || param2_false_boolean) {\n              result.addMinTypeExprent(param1, VarType.VARTYPE_BYTECHAR);\n              result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR);\n            }\n          }\n        }\n        else if (type2.getType() == CodeConstants.TYPE_BOOLEAN) {\n          if (type1.isStrictSuperset(type2)) {\n            result.addMinTypeExprent(param2, VarType.VARTYPE_BYTECHAR);\n          }\n        }\n      }\n    }\n\n    return result;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    return new ArrayList<>(lstOperands);\n  }\n\n  @Override\n  public Exprent copy() {\n    List<Exprent> lst = new ArrayList<>();\n    for (Exprent expr : lstOperands) {\n      lst.add(expr.copy());\n    }\n    FunctionExprent func = new FunctionExprent(funcType, lst, bytecode);\n    func.setImplicitType(implicitType);\n\n    return func;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof FunctionExprent fe)) return false;\n\n    return funcType == fe.funcType &&\n           Objects.equals(lstOperands, fe.lstOperands); // TODO: order of operands insignificant\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    for (int i = 0; i < lstOperands.size(); i++) {\n      if (oldExpr == lstOperands.get(i)) {\n        lstOperands.set(i, newExpr);\n      }\n    }\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    tracer.addMapping(bytecode);\n\n    if (funcType <= FUNCTION_USHR) {\n      return wrapOperandString(lstOperands.get(0), false, indent, tracer)\n        .append(OPERATORS[funcType])\n        .append(wrapOperandString(lstOperands.get(1), true, indent, tracer));\n    }\n\n      // try to determine more accurate type for 'char' literals\n    if (funcType >= FUNCTION_EQ) {\n      if (funcType <= FUNCTION_LE) {\n        Exprent left = lstOperands.get(0);\n        Exprent right = lstOperands.get(1);\n\n        if (right.type == EXPRENT_CONST) {\n          ((ConstExprent) right).adjustConstType(left.getExprType());\n        }\n        else if (left.type == EXPRENT_CONST) {\n          ((ConstExprent) left).adjustConstType(right.getExprType());\n        }\n      }\n\n      return wrapOperandString(lstOperands.get(0), false, indent, tracer)\n        .append(OPERATORS[funcType - FUNCTION_EQ + 11])\n        .append(wrapOperandString(lstOperands.get(1), true, indent, tracer));\n    }\n\n    return switch (funcType) {\n      case FUNCTION_BIT_NOT -> wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"~\");\n      case FUNCTION_BOOL_NOT -> wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"!\");\n      case FUNCTION_NEG -> wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"-\");\n      case FUNCTION_CAST -> lstOperands.get(1).toJava(indent, tracer).enclose(\"(\", \")\")\n        .append(wrapOperandString(lstOperands.get(0), true, indent, tracer));\n      case FUNCTION_ARRAY_LENGTH -> {\n        Exprent arr = lstOperands.get(0);\n        TextBuffer res = wrapOperandString(arr, false, indent, tracer);\n        if (arr.getExprType().getArrayDim() == 0) {\n          VarType objArr = VarType.VARTYPE_OBJECT.resizeArrayDim(1); // type family does not change\n          res.enclose(\"((\" + ExprProcessor.getCastTypeName(objArr, Collections.emptyList()) + \")\", \")\");\n        }\n        yield res.append(\".length\");\n      }\n      case FUNCTION_IIF -> wrapOperandString(lstOperands.get(0), true, indent, tracer)\n        .append(\" ? \")\n        .append(wrapOperandString(lstOperands.get(1), true, indent, tracer))\n        .append(\" : \")\n        .append(wrapOperandString(lstOperands.get(2), true, indent, tracer));\n      case FUNCTION_IPP -> wrapOperandString(lstOperands.get(0), true, indent, tracer).append(\"++\");\n      case FUNCTION_PPI -> wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"++\");\n      case FUNCTION_IMM -> wrapOperandString(lstOperands.get(0), true, indent, tracer).append(\"--\");\n      case FUNCTION_MMI -> wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"--\");\n      case FUNCTION_INSTANCEOF -> wrapOperandString(lstOperands.get(0), true, indent, tracer).append(\" instanceof \")\n        .append(wrapOperandString(lstOperands.get(1), true, indent, tracer));\n      case FUNCTION_LCMP -> // shouldn't appear in the final code\n        wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"__lcmp__(\")\n          .append(\", \")\n          .append(wrapOperandString(lstOperands.get(1), true, indent, tracer))\n          .append(\")\");\n      case FUNCTION_FCMPL -> // shouldn't appear in the final code\n        wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"__fcmpl__(\")\n          .append(\", \")\n          .append(wrapOperandString(lstOperands.get(1), true, indent, tracer))\n          .append(\")\");\n      case FUNCTION_FCMPG -> // shouldn't appear in the final code\n        wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"__fcmpg__(\")\n          .append(\", \")\n          .append(wrapOperandString(lstOperands.get(1), true, indent, tracer))\n          .append(\")\");\n      case FUNCTION_DCMPL -> // shouldn't appear in the final code\n        wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"__dcmpl__(\")\n          .append(\", \")\n          .append(wrapOperandString(lstOperands.get(1), true, indent, tracer))\n          .append(\")\");\n      case FUNCTION_DCMPG -> // shouldn't appear in the final code\n        wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"__dcmpg__(\")\n          .append(\", \")\n          .append(wrapOperandString(lstOperands.get(1), true, indent, tracer))\n          .append(\")\");\n      default -> {\n        assert funcType <= FUNCTION_I2S;\n        yield wrapOperandString(lstOperands.get(0), true, indent, tracer).prepend(\"(\" + ExprProcessor.getTypeName(\n          TYPES[funcType - FUNCTION_I2L], Collections.emptyList()) + \")\");\n      }\n    };\n  }\n\n  @Override\n  public int getPrecedence() {\n    return getPrecedence(funcType);\n  }\n\n  public static int getPrecedence(int func) {\n    return PRECEDENCE[func];\n  }\n\n  public VarType getSimpleCastType() {\n    return TYPES[funcType - FUNCTION_I2L];\n  }\n\n  private TextBuffer wrapOperandString(Exprent expr, boolean eq, int indent, BytecodeMappingTracer tracer) {\n    int myprec = getPrecedence();\n    int exprprec = expr.getPrecedence();\n\n    boolean parentheses = exprprec > myprec;\n    if (!parentheses && eq) {\n      parentheses = (exprprec == myprec);\n      if (parentheses) {\n        if (expr.type == Exprent.EXPRENT_FUNCTION &&\n            ((FunctionExprent)expr).getFuncType() == funcType) {\n          parentheses = !ASSOCIATIVITY.contains(funcType);\n        }\n      }\n    }\n\n    TextBuffer res = expr.toJava(indent, tracer);\n\n    if (parentheses) {\n      res.enclose(\"(\", \")\");\n    }\n\n    return res;\n  }\n\n  private static VarType getMaxVarType(VarType[] arr) {\n    int[] types = {CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_FLOAT, CodeConstants.TYPE_LONG};\n    VarType[] vartypes = {VarType.VARTYPE_DOUBLE, VarType.VARTYPE_FLOAT, VarType.VARTYPE_LONG};\n\n    for (int i = 0; i < types.length; i++) {\n      for (VarType anArr : arr) {\n        if (anArr.getType() == types[i]) {\n          return vartypes[i];\n        }\n      }\n    }\n\n    return VarType.VARTYPE_INT;\n  }\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public int getFuncType() {\n    return funcType;\n  }\n\n  public void setFuncType(int funcType) {\n    this.funcType = funcType;\n  }\n\n  public List<Exprent> getLstOperands() {\n    return lstOperands;\n  }\n\n  public void setImplicitType(VarType implicitType) {\n    this.implicitType = implicitType;\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (!super.match(matchNode, engine)) {\n      return false;\n    }\n\n    Integer type = (Integer)matchNode.getRuleValue(MatchProperties.EXPRENT_FUNCTYPE);\n    return type == null || this.funcType == type;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java",
    "content": "/*\n * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\n */\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.ListStack;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\npublic class IfExprent extends Exprent {\n\n  public static final int IF_EQ = 0;\n  public static final int IF_NE = 1;\n  public static final int IF_LT = 2;\n  public static final int IF_GE = 3;\n  public static final int IF_GT = 4;\n  public static final int IF_LE = 5;\n\n  public static final int IF_NULL = 6;\n  public static final int IF_NONNULL = 7;\n\n  public static final int IF_ICMPEQ = 8;\n  public static final int IF_ICMPNE = 9;\n  public static final int IF_ICMPLT = 10;\n  public static final int IF_ICMPGE = 11;\n  public static final int IF_ICMPGT = 12;\n  public static final int IF_ICMPLE = 13;\n  public static final int IF_ACMPEQ = 14;\n  public static final int IF_ACMPNE = 15;\n\n  //public static final int IF_CAND = 16;\n  //public static final int IF_COR = 17;\n  //public static final int IF_NOT = 18;\n  public static final int IF_VALUE = 19;\n\n  private static final int[] FUNC_TYPES = {\n    FunctionExprent.FUNCTION_EQ,\n    FunctionExprent.FUNCTION_NE,\n    FunctionExprent.FUNCTION_LT,\n    FunctionExprent.FUNCTION_GE,\n    FunctionExprent.FUNCTION_GT,\n    FunctionExprent.FUNCTION_LE,\n    FunctionExprent.FUNCTION_EQ,\n    FunctionExprent.FUNCTION_NE,\n    FunctionExprent.FUNCTION_EQ,\n    FunctionExprent.FUNCTION_NE,\n    FunctionExprent.FUNCTION_LT,\n    FunctionExprent.FUNCTION_GE,\n    FunctionExprent.FUNCTION_GT,\n    FunctionExprent.FUNCTION_LE,\n    FunctionExprent.FUNCTION_EQ,\n    FunctionExprent.FUNCTION_NE,\n    FunctionExprent.FUNCTION_CADD,\n    FunctionExprent.FUNCTION_COR,\n    FunctionExprent.FUNCTION_BOOL_NOT,\n    -1\n  };\n\n  private Exprent condition;\n\n  public IfExprent(int ifType, ListStack<Exprent> stack, Set<Integer> bytecodeOffsets) {\n    this(null, bytecodeOffsets);\n\n    if (ifType <= IF_LE) {\n      stack.push(new ConstExprent(0, true, null));\n    }\n    else if (ifType <= IF_NONNULL) {\n      stack.push(new ConstExprent(VarType.VARTYPE_NULL, null, null));\n    }\n\n    if (ifType == IF_VALUE) {\n      condition = stack.pop();\n    }\n    else {\n      condition = new FunctionExprent(FUNC_TYPES[ifType], stack, bytecodeOffsets);\n    }\n  }\n\n  private IfExprent(Exprent condition, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_IF);\n    this.condition = condition;\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  @Override\n  public Exprent copy() {\n    return new IfExprent(condition.copy(), bytecode);\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n    lst.add(condition);\n    return lst;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    tracer.addMapping(bytecode);\n    return condition.toJava(indent, tracer).enclose(\"if (\", \")\");\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == condition) {\n      condition = newExpr;\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof IfExprent ie)) return false;\n\n    return Objects.equals(condition, ie.getCondition());\n  }\n\n  public IfExprent negateIf() {\n    condition = new FunctionExprent(FunctionExprent.FUNCTION_BOOL_NOT, condition, condition.bytecode);\n    return this;\n  }\n\n  public Exprent getCondition() {\n    return condition;\n  }\n\n  public void setCondition(Exprent condition) {\n    this.condition = condition;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ClasspathHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.consts.LinkConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PooledConstant;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\nimport org.jetbrains.java.decompiler.util.ListStack;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\nimport org.jetbrains.java.decompiler.util.TextUtil;\n\nimport java.lang.reflect.Method;\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class InvocationExprent extends Exprent {\n  private static final int INVOKE_SPECIAL = 1;\n  private static final int INVOKE_VIRTUAL = 2;\n  private static final int INVOKE_STATIC = 3;\n  private static final int INVOKE_INTERFACE = 4;\n  public static final int INVOKE_DYNAMIC = 5;\n\n  public static final int TYPE_GENERAL = 1;\n  public static final int TYPE_INIT = 2;\n  public static final int TYPE_CLINIT = 3;\n\n  private static final BitSet EMPTY_BIT_SET = new BitSet(0);\n\n  private String name;\n  private String className;\n  private boolean isStatic;\n  private boolean canIgnoreBoxing = true;\n  private int funcType = TYPE_GENERAL;\n  private Exprent instance;\n  private MethodDescriptor descriptor;\n  private String stringDescriptor;\n  private String invokeDynamicClassSuffix;\n  private int invocationType = INVOKE_VIRTUAL;\n  private List<Exprent> parameters = new ArrayList<>();\n  private List<PooledConstant> bootstrapArguments;\n\n  public InvocationExprent() {\n    super(EXPRENT_INVOCATION);\n  }\n\n  public InvocationExprent(int opcode,\n                           LinkConstant cn,\n                           List<PooledConstant> bootstrapArguments,\n                           ListStack<? extends Exprent> stack,\n                           Set<Integer> bytecodeOffsets) {\n    this();\n\n    name = cn.elementName;\n    className = cn.className;\n    this.bootstrapArguments = bootstrapArguments;\n    switch (opcode) {\n      case CodeConstants.opc_invokestatic -> invocationType = INVOKE_STATIC;\n      case CodeConstants.opc_invokespecial -> invocationType = INVOKE_SPECIAL;\n      case CodeConstants.opc_invokevirtual -> invocationType = INVOKE_VIRTUAL;\n      case CodeConstants.opc_invokeinterface -> invocationType = INVOKE_INTERFACE;\n      case CodeConstants.opc_invokedynamic -> {\n        invocationType = INVOKE_DYNAMIC;\n\n        className = \"java/lang/Class\"; // dummy class name\n        invokeDynamicClassSuffix = \"##Lambda_\" + cn.index1 + \"_\" + cn.index2;\n      }\n    }\n\n    if (CodeConstants.INIT_NAME.equals(name)) {\n      funcType = TYPE_INIT;\n    }\n    else if (CodeConstants.CLINIT_NAME.equals(name)) {\n      funcType = TYPE_CLINIT;\n    }\n\n    stringDescriptor = cn.descriptor;\n    descriptor = MethodDescriptor.parseDescriptor(cn.descriptor);\n\n    for (VarType ignored : descriptor.params) {\n      parameters.add(0, stack.pop());\n    }\n\n    if (opcode == CodeConstants.opc_invokedynamic) {\n      int dynamicInvocationType = -1;\n      if (bootstrapArguments != null) {\n        if (bootstrapArguments.size() > 1) { // INVOKEDYNAMIC is used not only for lambdas\n          PooledConstant link = bootstrapArguments.get(1);\n          if (link instanceof LinkConstant) {\n            dynamicInvocationType = ((LinkConstant)link).index1;\n          }\n        }\n      }\n      if (dynamicInvocationType == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) {\n        isStatic = true;\n      }\n      else {\n        // FIXME: remove the first parameter completely from the list. It's the object type for a virtual lambda method.\n        if (!parameters.isEmpty()) {\n          instance = parameters.get(0);\n        }\n      }\n    }\n    else if (opcode == CodeConstants.opc_invokestatic) {\n      isStatic = true;\n    }\n    else {\n      instance = stack.pop();\n    }\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  private InvocationExprent(InvocationExprent expr) {\n    this();\n\n    name = expr.getName();\n    className = expr.getClassName();\n    isStatic = expr.isStatic();\n    canIgnoreBoxing = expr.canIgnoreBoxing;\n    funcType = expr.getFuncType();\n    instance = expr.getInstance();\n    if (instance != null) {\n      instance = instance.copy();\n    }\n    invocationType = expr.getInvocationType();\n    invokeDynamicClassSuffix = expr.getInvokeDynamicClassSuffix();\n    stringDescriptor = expr.getStringDescriptor();\n    descriptor = expr.getDescriptor();\n\n    List<Exprent> parameters = expr.getParameters();\n    this.parameters = new ArrayList<>(parameters.size());\n    for (Exprent parameter : parameters) this.parameters.add(parameter.copy());\n\n    addBytecodeOffsets(expr.bytecode);\n    bootstrapArguments = expr.getBootstrapArguments();\n  }\n\n  @Override\n  public VarType getExprType() {\n    return descriptor.ret;\n  }\n\n  @Override\n  public CheckTypesResult checkExprTypeBounds() {\n    CheckTypesResult result = new CheckTypesResult();\n\n    for (int i = 0; i < parameters.size(); i++) {\n      Exprent parameter = parameters.get(i);\n\n      VarType leftType = descriptor.params[i];\n\n      result.addMinTypeExprent(parameter, VarType.getMinTypeInFamily(leftType.getTypeFamily()));\n      result.addMaxTypeExprent(parameter, leftType);\n    }\n\n    return result;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n    if (instance != null) {\n      lst.add(instance);\n    }\n    lst.addAll(parameters);\n    return lst;\n  }\n\n\n  @Override\n  public Exprent copy() {\n    return new InvocationExprent(this);\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n\n    String super_qualifier = null;\n    boolean isInstanceThis = false;\n\n    tracer.addMapping(bytecode);\n\n    if (instance instanceof InvocationExprent) {\n      ((InvocationExprent) instance).markUsingBoxingResult();\n    }\n\n    if (isStatic) {\n      if (isBoxingCall() && canIgnoreBoxing) {\n        // process general \"boxing\" calls, e.g. 'Object[] data = { true }' or 'Byte b = 123'\n        // here 'byte' and 'short' values do not need an explicit narrowing type cast\n        ExprProcessor.getCastedExprent(parameters.get(0), descriptor.params[0], buf, indent, false, false, false, false, tracer);\n        return buf;\n      }\n\n      ClassNode node = (ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);\n      if (node == null || !className.equals(node.classStruct.qualifiedName)) {\n        buf.append(DecompilerContext.getImportCollector().getNestedNameInClassContext(ExprProcessor.buildJavaClassName(className)));\n      }\n    }\n    else {\n\n      if (instance != null && instance.type == EXPRENT_VAR) {\n        VarExprent instVar = (VarExprent)instance;\n        VarVersionPair varPair = new VarVersionPair(instVar);\n\n        VarProcessor varProc = instVar.getProcessor();\n        if (varProc == null) {\n          MethodWrapper currentMethod = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);\n          if (currentMethod != null) {\n            varProc = currentMethod.varproc;\n          }\n        }\n\n        String this_classname = null;\n        if (varProc != null) {\n          this_classname = varProc.getThisVars().get(varPair);\n        }\n\n        if (this_classname != null) {\n          isInstanceThis = true;\n\n          if (invocationType == INVOKE_SPECIAL) {\n            if (!className.equals(this_classname)) { // TODO: direct comparison to the super class?\n              StructClass cl = DecompilerContext.getStructContext().getClass(className);\n              boolean isInterface = cl != null && cl.hasModifier(CodeConstants.ACC_INTERFACE);\n              super_qualifier = !isInterface ? this_classname : className;\n            }\n          }\n        }\n      }\n\n      if (funcType == TYPE_GENERAL) {\n        if (super_qualifier != null) {\n          TextUtil.writeQualifiedSuper(buf, super_qualifier);\n        }\n        else if (instance != null) {\n          TextBuffer res = instance.toJava(indent, tracer);\n\n          if (isUnboxingCall()) {\n            // we don't print the unboxing call - no need to bother with the instance wrapping / casting\n            buf.append(res);\n            return buf;\n          }\n\n          VarType rightType = instance.getExprType();\n          VarType leftType = new VarType(CodeConstants.TYPE_OBJECT, 0, className);\n\n          if (!leftType.equals(rightType) &&\n              (rightType.equals(VarType.VARTYPE_OBJECT) ||\n               //try to preserve for navigation in certain cases: virtual call on variable\n               (rightType.getType() != CodeConstants.TYPE_UNKNOWN &&\n                instance.type == EXPRENT_VAR &&\n                invocationType == INVOKE_VIRTUAL &&\n                !leftType.equals(VarType.VARTYPE_OBJECT)))) {\n            buf.append(\"((\").append(ExprProcessor.getCastTypeName(leftType, Collections.emptyList())).append(\")\");\n\n            if (instance.getPrecedence() >= FunctionExprent.getPrecedence(FunctionExprent.FUNCTION_CAST)) {\n              res.enclose(\"(\", \")\");\n            }\n            buf.append(res).append(\")\");\n          }\n          else if (instance.getPrecedence() > getPrecedence()) {\n            buf.append(\"(\").append(res).append(\")\");\n          }\n          else {\n            buf.append(res);\n          }\n        }\n      }\n    }\n\n    switch (funcType) {\n      case TYPE_GENERAL -> {\n        if (VarExprent.VAR_NAMELESS_ENCLOSURE.equals(buf.toString())) {\n          buf = new TextBuffer();\n        }\n\n        if (buf.length() > 0) {\n          buf.append(\".\");\n        }\n\n        buf.append(name);\n        if (invocationType == INVOKE_DYNAMIC) {\n          buf.append(\"<invokedynamic>\");\n        }\n        buf.append(\"(\");\n      }\n      case TYPE_CLINIT -> throw new RuntimeException(\"Explicit invocation of \" + CodeConstants.CLINIT_NAME);\n      case TYPE_INIT -> {\n        if (super_qualifier != null) {\n          buf.append(\"super(\");\n        }\n        else if (isInstanceThis) {\n          buf.append(\"this(\");\n        }\n        else if (instance != null) {\n          buf.append(instance.toJava(indent, tracer)).append(\".<init>(\");\n        }\n        else {\n          throw new RuntimeException(\"Unrecognized invocation of \" + CodeConstants.INIT_NAME);\n        }\n      }\n    }\n\n    List<VarVersionPair> mask = null;\n    boolean isEnum = false;\n    if (funcType == TYPE_INIT) {\n      ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(className);\n      if (newNode != null) {\n        mask = ExprUtil.getSyntheticParametersMask(newNode, stringDescriptor, parameters.size());\n        isEnum = newNode.classStruct.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);\n      }\n    }\n\n    BitSet setAmbiguousParameters = getAmbiguousParameters();\n\n    // omit 'new Type[] {}' for the last parameter of a vararg method call\n    if (parameters.size() == descriptor.params.length && isVarArgCall()) {\n      Exprent lastParam = parameters.get(parameters.size() - 1);\n      if (lastParam.type == EXPRENT_NEW && lastParam.getExprType().getArrayDim() >= 1) {\n        ((NewExprent) lastParam).setVarArgParam(true);\n      }\n    }\n\n    boolean firstParameter = true;\n    int start = isEnum ? 2 : 0;\n    for (int i = start; i < parameters.size(); i++) {\n      if (mask == null || mask.get(i) == null) {\n        TextBuffer buff = new TextBuffer();\n        boolean ambiguous = setAmbiguousParameters.get(i);\n\n        // 'byte' and 'short' literals need an explicit narrowing type cast when used as a parameter\n        ExprProcessor.getCastedExprent(parameters.get(i), descriptor.params[i], buff, indent, true, ambiguous, true, true, tracer);\n\n        // the last \"new Object[0]\" in the vararg call is not printed\n        if (buff.length() > 0) {\n          if (!firstParameter) {\n            buf.append(\", \");\n          }\n          buf.append(buff);\n        }\n\n        firstParameter = false;\n      }\n    }\n\n    buf.append(')');\n\n    return buf;\n  }\n\n  private boolean isVarArgCall() {\n    StructClass cl = DecompilerContext.getStructContext().getClass(className);\n    if (cl != null) {\n      StructMethod mt = cl.getMethod(InterpreterUtil.makeUniqueKey(name, stringDescriptor));\n      if (mt != null) {\n        return mt.hasModifier(CodeConstants.ACC_VARARGS);\n      }\n    }\n    else {\n      // TODO: tap into IDEA indices to access libraries methods details\n\n      // try to check the class on the classpath\n      Method mtd = ClasspathHelper.findMethod(className, name, descriptor);\n      return mtd != null && mtd.isVarArgs();\n    }\n    return false;\n  }\n\n  public boolean isBoxingCall() {\n    if (isStatic && \"valueOf\".equals(name) && parameters.size() == 1) {\n      int paramType = parameters.get(0).getExprType().getType();\n\n      // special handling for ambiguous types\n      if (parameters.get(0).type == EXPRENT_CONST) {\n        // 'Integer.valueOf(1)' has '1' type detected as TYPE_BYTECHAR\n        // 'Integer.valueOf(40_000)' has '40_000' type detected as TYPE_CHAR\n        // so we check the type family instead\n        if (parameters.get(0).getExprType().getTypeFamily() == CodeConstants.TYPE_FAMILY_INTEGER) {\n          if (className.equals(\"java/lang/Integer\")) {\n            return true;\n          }\n        }\n\n        if (paramType == CodeConstants.TYPE_BYTECHAR || paramType == CodeConstants.TYPE_SHORTCHAR) {\n          if (className.equals(\"java/lang/Character\")) {\n            return true;\n          }\n        }\n      }\n\n      return className.equals(getClassNameForPrimitiveType(paramType));\n    }\n\n    return false;\n  }\n\n  public boolean isInstanceCall(@NotNull String className, @NotNull String methodName, int parametersCount) {\n    return invocationType == INVOKE_VIRTUAL &&\n           this.className.equals(className) && methodName.equals(name) && parameters.size() == parametersCount;\n  }\n\n  public boolean isDynamicCall(@NotNull String methodName, int parametersCount) {\n    return invocationType == INVOKE_DYNAMIC && methodName.equals(name) && parameters.size() == parametersCount;\n  }\n\n  public void markUsingBoxingResult() {\n    canIgnoreBoxing = false;\n  }\n\n  // TODO: move to CodeConstants ???\n  private static String getClassNameForPrimitiveType(int type) {\n    return switch (type) {\n      case CodeConstants.TYPE_BOOLEAN -> \"java/lang/Boolean\";\n      case CodeConstants.TYPE_BYTE, CodeConstants.TYPE_BYTECHAR -> \"java/lang/Byte\";\n      case CodeConstants.TYPE_CHAR -> \"java/lang/Character\";\n      case CodeConstants.TYPE_SHORT, CodeConstants.TYPE_SHORTCHAR -> \"java/lang/Short\";\n      case CodeConstants.TYPE_INT -> \"java/lang/Integer\";\n      case CodeConstants.TYPE_LONG -> \"java/lang/Long\";\n      case CodeConstants.TYPE_FLOAT -> \"java/lang/Float\";\n      case CodeConstants.TYPE_DOUBLE -> \"java/lang/Double\";\n      default -> null;\n    };\n  }\n\n  private static final Map<String, String> UNBOXING_METHODS = Map.of(\n    \"booleanValue\", \"java/lang/Boolean\",\n    \"byteValue\", \"java/lang/Byte\",\n    \"shortValue\", \"java/lang/Short\",\n    \"intValue\", \"java/lang/Integer\",\n    \"longValue\", \"java/lang/Long\",\n    \"floatValue\", \"java/lang/Float\",\n    \"doubleValue\", \"java/lang/Double\",\n    \"charValue\", \"java/lang/Character\"\n  );\n\n  private boolean isUnboxingCall() {\n    return !isStatic && parameters.isEmpty() && className.equals(UNBOXING_METHODS.get(name));\n  }\n\n  private BitSet getAmbiguousParameters() {\n    StructClass cl = DecompilerContext.getStructContext().getClass(className);\n    if (cl == null) return EMPTY_BIT_SET;\n\n    // check number of matches\n    List<MethodDescriptor> matches = new ArrayList<>();\n    nextMethod:\n    for (StructMethod mt : cl.getMethods()) {\n      if (name.equals(mt.getName())) {\n        MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n        if (md.params.length == descriptor.params.length) {\n          for (int i = 0; i < md.params.length; i++) {\n            if (md.params[i].getTypeFamily() != descriptor.params[i].getTypeFamily()) {\n              continue nextMethod;\n            }\n          }\n          matches.add(md);\n        }\n      }\n    }\n    if (matches.size() == 1) return EMPTY_BIT_SET;\n\n    // check if a call is unambiguous\n    StructMethod mt = cl.getMethod(InterpreterUtil.makeUniqueKey(name, stringDescriptor));\n    if (mt != null) {\n      MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n      if (md.params.length == parameters.size()) {\n        boolean exact = true;\n        for (int i = 0; i < md.params.length; i++) {\n          if (!md.params[i].equals(parameters.get(i).getExprType())) {\n            exact = false;\n            break;\n          }\n        }\n        if (exact) return EMPTY_BIT_SET;\n      }\n    }\n\n    // mark parameters\n    BitSet ambiguous = new BitSet(descriptor.params.length);\n    for (int i = 0; i < descriptor.params.length; i++) {\n      VarType paramType = descriptor.params[i];\n      for (MethodDescriptor md : matches) {\n        if (!paramType.equals(md.params[i])) {\n          ambiguous.set(i);\n          break;\n        }\n      }\n    }\n    return ambiguous;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == instance) {\n      instance = newExpr;\n    }\n\n    for (int i = 0; i < parameters.size(); i++) {\n      if (oldExpr == parameters.get(i)) {\n        parameters.set(i, newExpr);\n      }\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof InvocationExprent it)) return false;\n\n    return Objects.equals(name, it.name) &&\n           Objects.equals(className, it.className) &&\n           isStatic == it.isStatic &&\n           Objects.equals(instance, it.instance) &&\n           Objects.equals(descriptor, it.descriptor) &&\n           funcType == it.funcType &&\n           Objects.equals(parameters, it.parameters);\n  }\n\n  public List<Exprent> getParameters() {\n    return parameters;\n  }\n\n  public void setParameters(List<Exprent> parameters) {\n    this.parameters = parameters;\n  }\n\n  public MethodDescriptor getDescriptor() {\n    return descriptor;\n  }\n\n  public void setDescriptor(MethodDescriptor descriptor) {\n    this.descriptor = descriptor;\n  }\n\n  public String getClassName() {\n    return className;\n  }\n\n  public void setClassName(String className) {\n    this.className = className;\n  }\n\n  public int getFuncType() {\n    return funcType;\n  }\n\n  public void setFuncType(int funcType) {\n    this.funcType = funcType;\n  }\n\n  public Exprent getInstance() {\n    return instance;\n  }\n\n  public void setInstance(Exprent instance) {\n    this.instance = instance;\n  }\n\n  public boolean isStatic() {\n    return isStatic;\n  }\n\n  public void setStatic(boolean isStatic) {\n    this.isStatic = isStatic;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getStringDescriptor() {\n    return stringDescriptor;\n  }\n\n  public void setStringDescriptor(String stringDescriptor) {\n    this.stringDescriptor = stringDescriptor;\n  }\n\n  public int getInvocationType() {\n    return invocationType;\n  }\n\n  public String getInvokeDynamicClassSuffix() {\n    return invokeDynamicClassSuffix;\n  }\n\n  public List<PooledConstant> getBootstrapArguments() {\n    return bootstrapArguments;\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (!super.match(matchNode, engine)) {\n      return false;\n    }\n\n    for (Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {\n      RuleValue value = rule.getValue();\n\n      MatchProperties key = rule.getKey();\n      if (key == MatchProperties.EXPRENT_INVOCATION_PARAMETER) {\n        if (value.isVariable() && (value.parameter >= parameters.size() ||\n                                   !engine.checkAndSetVariableValue(value.value.toString(), parameters.get(value.parameter)))) {\n          return false;\n        }\n      }\n      else if (key == MatchProperties.EXPRENT_INVOCATION_CLASS) {\n        if (!value.value.equals(this.className)) {\n          return false;\n        }\n      }\n      else if (key == MatchProperties.EXPRENT_INVOCATION_SIGNATURE) {\n        if (!value.value.equals(this.name + this.stringDescriptor)) {\n          return false;\n        }\n      }\n    }\n\n    return true;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java",
    "content": "/*\n * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\n */\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\npublic class MonitorExprent extends Exprent {\n\n  public static final int MONITOR_ENTER = 0;\n  public static final int MONITOR_EXIT = 1;\n\n  private final int monType;\n  private Exprent value;\n\n  public MonitorExprent(int monType, Exprent value, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_MONITOR);\n    this.monType = monType;\n    this.value = value;\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  @Override\n  public Exprent copy() {\n    return new MonitorExprent(monType, value.copy(), bytecode);\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n    lst.add(value);\n    return lst;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    tracer.addMapping(bytecode);\n\n    if (monType == MONITOR_ENTER) {\n      return value.toJava(indent, tracer).enclose(\"synchronized(\", \")\");\n    }\n    else {\n      return new TextBuffer();\n    }\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == value) {\n      value = newExpr;\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof MonitorExprent me)) return false;\n\n    return monType == me.getMonType() &&\n           Objects.equals(value, me.getValue());\n  }\n\n  public int getMonType() {\n    return monType;\n  }\n\n  public Exprent getValue() {\n    return value;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassWriter;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.generics.GenericMain;\nimport org.jetbrains.java.decompiler.util.ListStack;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\n\npublic class NewExprent extends Exprent {\n  private InvocationExprent constructor;\n  private final VarType newType;\n  private final List<Exprent> lstDims;\n  private List<Exprent> lstArrayElements = new ArrayList<>();\n  private boolean directArrayInit;\n  private boolean isVarArgParam;\n  private boolean anonymous;\n  private boolean lambda;\n  private boolean enumConst;\n\n  public NewExprent(VarType newType, ListStack<Exprent> stack, int arrayDim, Set<Integer> bytecodeOffsets) {\n    this(newType, getDimensions(arrayDim, stack), bytecodeOffsets);\n  }\n\n  public NewExprent(VarType newType, List<Exprent> lstDims, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_NEW);\n    this.newType = newType;\n    this.lstDims = lstDims;\n\n    anonymous = false;\n    lambda = false;\n    if (newType.getType() == CodeConstants.TYPE_OBJECT && newType.getArrayDim() == 0) {\n      ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.getValue());\n      if (node != null && (node.type == ClassNode.CLASS_ANONYMOUS || node.type == ClassNode.CLASS_LAMBDA)) {\n        anonymous = true;\n        if (node.type == ClassNode.CLASS_LAMBDA) {\n          lambda = true;\n        }\n      }\n    }\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  private static List<Exprent> getDimensions(int arrayDim, ListStack<Exprent> stack) {\n    List<Exprent> lstDims = new ArrayList<>();\n    for (int i = 0; i < arrayDim; i++) {\n      lstDims.add(0, stack.pop());\n    }\n    return lstDims;\n  }\n\n  @Override\n  public VarType getExprType() {\n    return anonymous ? DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.getValue()).anonymousClassType : newType;\n  }\n\n  @Override\n  public CheckTypesResult checkExprTypeBounds() {\n    CheckTypesResult result = new CheckTypesResult();\n\n    if (newType.getArrayDim() != 0) {\n      for (Exprent dim : lstDims) {\n        result.addMinTypeExprent(dim, VarType.VARTYPE_BYTECHAR);\n        result.addMaxTypeExprent(dim, VarType.VARTYPE_INT);\n      }\n\n      if (newType.getArrayDim() == 1) {\n        VarType leftType = newType.decreaseArrayDim();\n        for (Exprent element : lstArrayElements) {\n          result.addMinTypeExprent(element, VarType.getMinTypeInFamily(leftType.getTypeFamily()));\n          result.addMaxTypeExprent(element, leftType);\n        }\n      }\n    }\n    else if (constructor != null) {\n      return constructor.checkExprTypeBounds();\n    }\n\n    return result;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n\n    if (newType.getArrayDim() != 0) {\n      lst.addAll(lstDims);\n      lst.addAll(lstArrayElements);\n    }\n    else if (constructor != null) {\n      Exprent constructor = this.constructor.getInstance();\n      if (constructor != null) { // should be true only for a lambda expression with a virtual content method\n        lst.add(constructor);\n      }\n      lst.addAll(this.constructor.getParameters());\n    }\n\n    return lst;\n  }\n\n  @Override\n  public Exprent copy() {\n    List<Exprent> lst = new ArrayList<>();\n    for (Exprent expr : lstDims) {\n      lst.add(expr.copy());\n    }\n\n    NewExprent ret = new NewExprent(newType, lst, bytecode);\n    ret.setConstructor(constructor == null ? null : (InvocationExprent)constructor.copy());\n    ret.setLstArrayElements(lstArrayElements);\n    ret.setDirectArrayInit(directArrayInit);\n    ret.setAnonymous(anonymous);\n    ret.setEnumConst(enumConst);\n    return ret;\n  }\n\n  @Override\n  public int getPrecedence() {\n    return 1; // precedence of new\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n\n    if (anonymous) {\n      ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.getValue());\n\n      boolean selfReference = DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE) == child;\n\n      // IDEA-204310 - avoid backtracking later on for lambdas (causes spurious imports)\n      if (!enumConst && (!lambda || DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS))) {\n        String enclosing = null;\n\n        if (!lambda && constructor != null) {\n          enclosing = getQualifiedNewInstance(child.anonymousClassType.getValue(), constructor.getParameters(), indent, tracer);\n          if (enclosing != null) {\n            buf.append(enclosing).append('.');\n          }\n        }\n\n        buf.append(\"new \");\n\n        if (selfReference) {\n          buf.append(\"<anonymous constructor>\");\n        } else {\n          String typename = ExprProcessor.getCastTypeName(child.anonymousClassType, Collections.emptyList());\n          if (enclosing != null) {\n            ClassNode anonymousNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(child.anonymousClassType.getValue());\n            if (anonymousNode != null) {\n              typename = anonymousNode.simpleName;\n            }\n            else {\n              typename = typename.substring(typename.lastIndexOf('.') + 1);\n            }\n          }\n\n          GenericClassDescriptor descriptor = ClassWriter.getGenericClassDescriptor(child.classStruct);\n          if (descriptor != null) {\n            if (descriptor.superinterfaces.isEmpty()) {\n              buf.append(GenericMain.getGenericCastTypeName(descriptor.superclass, Collections.emptyList()));\n            }\n            else {\n              if (descriptor.superinterfaces.size() > 1 && !lambda) {\n                DecompilerContext.getLogger().writeMessage(\"Inconsistent anonymous class signature: \" + child.classStruct.qualifiedName,\n                                                           IFernflowerLogger.Severity.WARN);\n              }\n              buf.append(GenericMain.getGenericCastTypeName(descriptor.superinterfaces.get(0), Collections.emptyList()));\n            }\n          }\n          else {\n            buf.append(typename);\n          }\n        }\n      }\n\n      buf.append('(');\n\n      if (!lambda && constructor != null) {\n        List<Exprent> parameters = constructor.getParameters();\n        List<VarVersionPair> mask = child.getWrapper().getMethodWrapper(CodeConstants.INIT_NAME, constructor.getStringDescriptor()).synthParameters;\n        if (mask == null) {\n          InvocationExprent superCall = child.superInvocation;\n          mask = ExprUtil.getSyntheticParametersMask(superCall.getClassName(), superCall.getStringDescriptor(), parameters.size());\n        }\n\n        int start = enumConst ? 2 : 0;\n        boolean firstParam = true;\n        for (int i = start; i < parameters.size(); i++) {\n          if (mask == null || mask.get(i) == null) {\n            if (!firstParam) {\n              buf.append(\", \");\n            }\n\n            ExprProcessor.getCastedExprent(parameters.get(i), constructor.getDescriptor().params[i], buf, indent, true, tracer);\n\n            firstParam = false;\n          }\n        }\n      }\n\n      buf.append(')');\n\n      if (enumConst && buf.length() == 2) {\n        buf.setLength(0);\n      }\n\n      if (lambda) {\n        if (!DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) {\n          buf.setLength(0);  // remove the usual 'new <class>()', it will be replaced with lambda style '() ->'\n        }\n        Exprent methodObject = constructor == null ? null : constructor.getInstance();\n        TextBuffer clsBuf = new TextBuffer();\n        new ClassWriter().classLambdaToJava(child, clsBuf, methodObject, indent, tracer);\n        buf.append(clsBuf);\n        tracer.incrementCurrentSourceLine(clsBuf.countLines());\n      }\n      else if (!selfReference) {\n        TextBuffer clsBuf = new TextBuffer();\n        new ClassWriter().classToJava(child, clsBuf, indent, tracer);\n        buf.append(clsBuf);\n        tracer.incrementCurrentSourceLine(clsBuf.countLines());\n      }\n    }\n    else if (directArrayInit) {\n      VarType leftType = newType.decreaseArrayDim();\n      buf.append('{');\n      for (int i = 0; i < lstArrayElements.size(); i++) {\n        if (i > 0) {\n          buf.append(\", \");\n        }\n        ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buf, indent, false, tracer);\n      }\n      buf.append('}');\n    }\n    else if (newType.getArrayDim() == 0) {\n      if (!enumConst) {\n        String enclosing = null;\n\n        if (constructor != null) {\n          enclosing = getQualifiedNewInstance(newType.getValue(), constructor.getParameters(), indent, tracer);\n          if (enclosing != null) {\n            buf.append(enclosing).append('.');\n          }\n        }\n\n        buf.append(\"new \");\n\n        String typename = ExprProcessor.getTypeName(newType, Collections.emptyList());\n        if (enclosing != null) {\n          ClassNode newNode = DecompilerContext.getClassProcessor().getMapRootClasses().get(newType.getValue());\n          if (newNode != null) {\n            typename = newNode.simpleName;\n          }\n          else {\n            typename = typename.substring(typename.lastIndexOf('.') + 1);\n          }\n        }\n        buf.append(typename);\n      }\n\n      if (constructor != null) {\n        List<Exprent> parameters = constructor.getParameters();\n        List<VarVersionPair> mask = ExprUtil.getSyntheticParametersMask(constructor.getClassName(), constructor.getStringDescriptor(), parameters.size());\n\n        int start = enumConst ? 2 : 0;\n        if (!enumConst || start < parameters.size()) {\n          buf.append('(');\n\n          boolean firstParam = true;\n          for (int i = start; i < parameters.size(); i++) {\n            if (mask == null || mask.get(i) == null) {\n              Exprent expr = parameters.get(i);\n              VarType leftType = constructor.getDescriptor().params[i];\n\n              if (i == parameters.size() - 1 && expr.getExprType() == VarType.VARTYPE_NULL && probablySyntheticParameter(\n                leftType.getValue())) {\n                break;  // skip last parameter of synthetic constructor call\n              }\n\n              if (!firstParam) {\n                buf.append(\", \");\n              }\n\n              ExprProcessor.getCastedExprent(expr, leftType, buf, indent, true, false, true, true, tracer);\n\n              firstParam = false;\n            }\n          }\n\n          buf.append(')');\n        }\n      }\n    }\n    else if (isVarArgParam) {\n      // just print the array elements\n      VarType leftType = newType.decreaseArrayDim();\n      for (int i = 0; i < lstArrayElements.size(); i++) {\n        if (i > 0) {\n          buf.append(\", \");\n        }\n\n        // new String[][]{{\"abc\"}, {\"DEF\"}} => new String[]{\"abc\"}, new String[]{\"DEF\"}\n        Exprent element = lstArrayElements.get(i);\n        if (element.type == EXPRENT_NEW) {\n          ((NewExprent) element).setDirectArrayInit(false);\n        }\n        ExprProcessor.getCastedExprent(element, leftType, buf, indent, false, tracer);\n      }\n\n      // if there is just one element of Object[] type it needs to be casted to resolve ambiguity\n      if (lstArrayElements.size() == 1) {\n        VarType elementType = lstArrayElements.get(0).getExprType();\n        if (elementType.getType() == CodeConstants.TYPE_OBJECT && elementType.getValue().equals(\"java/lang/Object\") && elementType.getArrayDim() >= 1) {\n          buf.prepend(\"(Object)\");\n        }\n      }\n    }\n    else {\n      buf.append(\"new \").append(ExprProcessor.getTypeName(newType, Collections.emptyList()));\n\n      if (lstArrayElements.isEmpty()) {\n        for (int i = 0; i < newType.getArrayDim(); i++) {\n          buf.append('[');\n          if (i < lstDims.size()) {\n            buf.append(lstDims.get(i).toJava(indent, tracer));\n          }\n          buf.append(']');\n        }\n      }\n      else {\n        for (int i = 0; i < newType.getArrayDim(); i++) {\n          buf.append(\"[]\");\n        }\n\n        VarType leftType = newType.decreaseArrayDim();\n        buf.append('{');\n        for (int i = 0; i < lstArrayElements.size(); i++) {\n          if (i > 0) {\n            buf.append(\", \");\n          }\n          ExprProcessor.getCastedExprent(lstArrayElements.get(i), leftType, buf, indent, false, tracer);\n        }\n        buf.append('}');\n      }\n    }\n\n    return buf;\n  }\n\n  private static boolean probablySyntheticParameter(String className) {\n    ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(className);\n    return node != null && node.type == ClassNode.CLASS_ANONYMOUS;\n  }\n\n  private static String getQualifiedNewInstance(String classname, List<Exprent> lstParams, int indent, BytecodeMappingTracer tracer) {\n    ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(classname);\n\n    if (node != null && node.type != ClassNode.CLASS_ROOT && node.type != ClassNode.CLASS_LOCAL\n        && (node.access & CodeConstants.ACC_STATIC) == 0) {\n      if (!lstParams.isEmpty()) {\n        Exprent enclosing = lstParams.get(0);\n\n        boolean isQualifiedNew = false;\n\n        if (enclosing.type == Exprent.EXPRENT_VAR) {\n          VarExprent varEnclosing = (VarExprent)enclosing;\n\n          StructClass current_class = ((ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE)).classStruct;\n          String this_classname = varEnclosing.getProcessor().getThisVars().get(new VarVersionPair(varEnclosing));\n\n          if (!current_class.qualifiedName.equals(this_classname)) {\n            isQualifiedNew = true;\n          }\n        }\n        else {\n          isQualifiedNew = true;\n        }\n\n        if (isQualifiedNew) {\n          return enclosing.toJava(indent, tracer).toString();\n        }\n      }\n    }\n\n    return null;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == constructor) {\n      constructor = (InvocationExprent)newExpr;\n    }\n\n    if (constructor != null) {\n      constructor.replaceExprent(oldExpr, newExpr);\n    }\n\n    for (int i = 0; i < lstDims.size(); i++) {\n      if (oldExpr == lstDims.get(i)) {\n        lstDims.set(i, newExpr);\n      }\n    }\n\n    for (int i = 0; i < lstArrayElements.size(); i++) {\n      if (oldExpr == lstArrayElements.get(i)) {\n        lstArrayElements.set(i, newExpr);\n      }\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof NewExprent ne)) return false;\n\n    return Objects.equals(newType, ne.newType) &&\n           Objects.equals(lstDims, ne.lstDims) &&\n           Objects.equals(constructor, ne.constructor) &&\n           directArrayInit == ne.directArrayInit &&\n           Objects.equals(lstArrayElements, ne.lstArrayElements);\n  }\n\n  public InvocationExprent getConstructor() {\n    return constructor;\n  }\n\n  public void setConstructor(InvocationExprent constructor) {\n    this.constructor = constructor;\n  }\n\n  public List<Exprent> getLstDims() {\n    return lstDims;\n  }\n\n  public VarType getNewType() {\n    return newType;\n  }\n\n  public List<Exprent> getLstArrayElements() {\n    return lstArrayElements;\n  }\n\n  public void setLstArrayElements(List<Exprent> lstArrayElements) {\n    this.lstArrayElements = lstArrayElements;\n  }\n\n  public void setDirectArrayInit(boolean directArrayInit) {\n    this.directArrayInit = directArrayInit;\n  }\n\n  public void setVarArgParam(boolean isVarArgParam) {\n    this.isVarArgParam = isVarArgParam;\n  }\n\n  public boolean isLambda() {\n    return lambda;\n  }\n\n  public boolean isAnonymous() {\n    return anonymous;\n  }\n\n  public void setAnonymous(boolean anonymous) {\n    this.anonymous = anonymous;\n  }\n\n  public void setEnumConst(boolean enumConst) {\n    this.enumConst = enumConst;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/RecordVarExprent.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Represents record deconstruction\n */\npublic class RecordVarExprent extends VarExprent {\n\n  @NotNull\n  private final List<RecordVarExprent> components = new ArrayList<>();\n\n  public RecordVarExprent(VarExprent v) {\n    super(v.getIndex(), v.getVarType(), v.getProcessor());\n    if (v.isClassDef()) {\n      throw new UnsupportedOperationException(\"Expect only var definition\");\n    }\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    if (components.isEmpty()) {\n      setDefinition(true);\n      return super.toJava(indent, tracer);\n    }\n    TextBuffer buffer = new TextBuffer();\n\n    appendDefinitionType(buffer);\n\n    buffer.append(\"(\");\n    for (int i = 0; i < components.size(); i++) {\n      buffer.append(components.get(i).toJava(0, tracer));\n      if (i != components.size() - 1) {\n        buffer.append(\", \");\n      }\n    }\n    buffer.append(\")\");\n    return buffer;\n  }\n\n  public void addComponent(@NotNull RecordVarExprent exprent) {\n    components.add(exprent);\n  }\n\n  /**\n   * @return a copy of the current RecordVarExprent object, including a copy of its VarExprent superclass and components\n   */\n  @Override\n  public RecordVarExprent copy() {\n    VarExprent copy = (VarExprent)super.copy();\n    RecordVarExprent newRoot = new RecordVarExprent(copy);\n    for (RecordVarExprent component : components) {\n      newRoot.addComponent(component.copy());\n    }\n    return newRoot;\n  }\n\n  /**\n   * Copies the values from the provided VarExprent object to the current RecordVarExprent object.\n   * If the provided VarExprent object is an instance of RecordVarExprent, copies the components from it.\n   * Otherwise, copy the varType, index, and version from the provided VarExprent object.\n   *\n   * @param varExprent the VarExprent object to copy the values from\n   * @return true if the copying is successful (the current set of components is empty), false otherwise\n   */\n  public boolean copyFrom(@NotNull VarExprent varExprent) {\n    if (varExprent instanceof RecordVarExprent recordVarExprent) {\n      if (!this.components.isEmpty()) {\n        return false;\n      }\n      this.components.addAll(recordVarExprent.components);\n    }\n    this.setVarType(varExprent.getVarType());\n    this.setIndex(varExprent.getIndex());\n    this.setVersion(varExprent.getVersion());\n    return true;\n  }\n\n  public List<RecordVarExprent> getComponents() {\n    return new ArrayList<>(components);\n  }\n\n  /**\n   * Retrieves the direct component of a RecordVarExprent that equals the provided Exprent.\n   *\n   * @param exprent the Exprent to match\n   * @return the direct component of the RecordVarExprent that matches the provided Exprent, or null if no match is found\n   */\n  @Nullable\n  public RecordVarExprent getDirectComponent(@Nullable Exprent exprent) {\n    for (RecordVarExprent component : components) {\n      if (component.equals(exprent)) {\n        return component;\n      }\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java",
    "content": "/*\n * Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\n */\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;\nimport org.jetbrains.java.decompiler.struct.consts.PooledConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport static org.jetbrains.java.decompiler.modules.decompiler.SwitchPatternHelper.isBootstrapSwitch;\n\npublic class SwitchExprent extends Exprent {\n\n  private Exprent value;\n  private List<List<Exprent>> caseValues = new ArrayList<>();\n\n  public SwitchExprent(Exprent value, Set<Integer> bytecodeOffsets) {\n    super(EXPRENT_SWITCH);\n    this.value = value;\n\n    addBytecodeOffsets(bytecodeOffsets);\n  }\n\n  @Override\n  public Exprent copy() {\n    SwitchExprent swExpr = new SwitchExprent(value.copy(), bytecode);\n\n    List<List<Exprent>> lstCaseValues = new ArrayList<>();\n    for (List<Exprent> lst : caseValues) {\n      lstCaseValues.add(new ArrayList<>(lst));\n    }\n    swExpr.setCaseValues(lstCaseValues);\n\n    return swExpr;\n  }\n\n  @Override\n  public VarType getExprType() {\n    return value.getExprType();\n  }\n\n  @Override\n  public CheckTypesResult checkExprTypeBounds() {\n    CheckTypesResult result = new CheckTypesResult();\n\n    result.addMinTypeExprent(value, VarType.VARTYPE_BYTECHAR);\n    result.addMaxTypeExprent(value, VarType.VARTYPE_INT);\n\n    VarType valType = value.getExprType();\n    for (List<Exprent> lst : caseValues) {\n      for (Exprent expr : lst) {\n        if (expr != null) {\n          VarType caseType = expr.getExprType();\n          if (!caseType.equals(valType)) {\n            valType = VarType.getCommonSupertype(caseType, valType);\n            result.addMinTypeExprent(value, valType);\n          }\n        }\n      }\n    }\n\n    return result;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    List<Exprent> lst = new ArrayList<>();\n    lst.add(value);\n    return lst;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    tracer.addMapping(bytecode);\n    //if it is impossible to process\n    TextBuffer buf = new TextBuffer();\n    if (isBootstrapSwitch(this)) {\n      InvocationExprent invocationExprent = (InvocationExprent)value;\n      List<Exprent> parameters = invocationExprent.getParameters();\n      if (parameters.size() == 2) {\n        Exprent exprent = parameters.get(1);\n        buf.append(\"//$FF: \").append(exprent.toJava(indent, tracer)).append(\"->\").append(\"value\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n        List<PooledConstant> arguments = invocationExprent.getBootstrapArguments();\n        for (int i = 0; i < arguments.size(); i++) {\n          PooledConstant argument = arguments.get(i);\n          if (argument instanceof PrimitiveConstant primitiveConstant && primitiveConstant.value != null) {\n            buf.appendIndent(indent).append(\"//\").append(i).append(\"->\").append(primitiveConstant.value.toString()).appendLineSeparator();\n            tracer.incrementCurrentSourceLine();\n          }\n        }\n        buf.appendIndent(indent);\n      }\n    }\n    buf.append(value.toJava(indent, tracer).enclose(\"switch (\", \")\"));\n    return buf;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (oldExpr == value) {\n      value = newExpr;\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) {\n      return true;\n    }\n\n    if (!(o instanceof SwitchExprent sw)) {\n      return false;\n    }\n\n    return Objects.equals(value, sw.getValue());\n  }\n\n  public Exprent getValue() {\n    return value;\n  }\n\n  public void setCaseValues(List<List<Exprent>> caseValues) {\n    this.caseValues = caseValues;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.exps;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassWriter;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTypeTableAttribute;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.generics.GenericMain;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\nimport org.jetbrains.java.decompiler.util.TextUtil;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class VarExprent extends Exprent {\n  public static final int STACK_BASE = 10000;\n  public static final String VAR_NAMELESS_ENCLOSURE = \"<VAR_NAMELESS_ENCLOSURE>\";\n\n  private int index;\n  private VarType varType;\n  private boolean definition = false;\n  private final VarProcessor processor;\n  private final int visibleOffset;\n  private int version = 0;\n  private boolean classDef = false;\n  private boolean stack = false;\n\n  public VarExprent(int index, VarType varType, VarProcessor processor) {\n    this(index, varType, processor, -1);\n  }\n\n  public VarExprent(int index, VarType varType, VarProcessor processor, int visibleOffset) {\n    super(EXPRENT_VAR);\n    this.index = index;\n    this.varType = varType;\n    this.processor = processor;\n    this.visibleOffset = visibleOffset;\n  }\n\n  @Override\n  public VarType getExprType() {\n    return getVarType();\n  }\n\n  @Override\n  public int getExprentUse() {\n    return Exprent.MULTIPLE_USES | Exprent.SIDE_EFFECTS_FREE;\n  }\n\n  @Override\n  public List<Exprent> getAllExprents() {\n    return new ArrayList<>();\n  }\n\n  @Override\n  public Exprent copy() {\n    VarExprent var = new VarExprent(index, getVarType(), processor, visibleOffset);\n    var.setDefinition(definition);\n    var.setVersion(version);\n    var.setClassDef(classDef);\n    var.setStack(stack);\n    return var;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buffer = new TextBuffer();\n\n    tracer.addMapping(bytecode);\n\n    if (classDef) {\n      ClassNode child = DecompilerContext.getClassProcessor().getMapRootClasses().get(varType.getValue());\n      new ClassWriter().classToJava(child, buffer, indent, tracer);\n      tracer.incrementCurrentSourceLine(buffer.countLines());\n    }\n    else {\n      VarVersionPair varVersion = getVarVersionPair();\n      String name = null;\n      if (processor != null) {\n        name = processor.getVarName(varVersion);\n      }\n\n      if (definition) {\n        if (processor != null && processor.getVarFinal(varVersion) == VarProcessor.VAR_EXPLICIT_FINAL) {\n          buffer.append(\"final \");\n        }\n        appendDefinitionType(buffer);\n        buffer.append(\" \");\n      }\n\n      buffer.append(name == null ? getName(getVarVersionPair()) : name);\n    }\n\n    return buffer;\n  }\n\n  public int getVisibleOffset() {\n    return visibleOffset;\n  }\n\n  @NotNull\n  public static String getName(VarVersionPair versionPair) {\n    return \"var\" + versionPair.var + (versionPair.version == 0 ? \"\" : \"_\" + versionPair.version);\n  }\n\n  public VarVersionPair getVarVersionPair() {\n    return new VarVersionPair(index, version);\n  }\n\n  public String getDebugName(StructMethod method) {\n    StructLocalVariableTableAttribute attr = method.getLocalVariableAttr();\n    if (attr != null && processor != null) {\n      Integer origIndex = processor.getVarOriginalIndex(index);\n      if (origIndex != null) {\n        String name = attr.getName(origIndex, visibleOffset);\n        if (name != null && TextUtil.isValidIdentifier(name, method.getBytecodeVersion())) {\n          return name;\n        }\n      }\n    }\n    return null;\n  }\n\n  void appendDefinitionType(TextBuffer buffer) {\n    if (DecompilerContext.getOption(IFernflowerPreferences.USE_DEBUG_VAR_NAMES)) {\n      MethodWrapper method = (MethodWrapper)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);\n      if (method != null) {\n        Integer originalIndex = null;\n        if (processor != null) {\n          originalIndex = processor.getVarOriginalIndex(index);\n        }\n        if (originalIndex != null) {\n          // first try from signature\n          if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {\n            StructLocalVariableTypeTableAttribute attr =\n              method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE);\n            if (attr != null) {\n              String signature = attr.getSignature(originalIndex, visibleOffset);\n              if (signature != null) {\n                GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(signature);\n                if (descriptor != null) {\n                  buffer.append(GenericMain.getGenericCastTypeName(descriptor.type, Collections.emptyList()));\n                  return;\n                }\n              }\n            }\n          }\n\n          // then try from descriptor\n          StructLocalVariableTableAttribute attr = method.methodStruct.getLocalVariableAttr();\n          if (attr != null) {\n            String descriptor = attr.getDescriptor(originalIndex, visibleOffset);\n            if (descriptor != null) {\n              buffer.append(ExprProcessor.getCastTypeName(new VarType(descriptor), Collections.emptyList()));\n              return;\n            }\n          }\n        }\n      }\n    }\n\n    buffer.append(ExprProcessor.getCastTypeName(getVarType(), Collections.emptyList()));\n  }\n\n  @Override\n  public int hashCode() {\n    return Objects.hash(index, version);\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof VarExprent ve)) return false;\n\n    return index == ve.getIndex() &&\n           version == ve.getVersion() &&\n           Objects.equals(getVarType(), ve.getVarType()); // FIXME: varType comparison redundant?\n  }\n\n  public int getIndex() {\n    return index;\n  }\n\n  public void setIndex(int index) {\n    this.index = index;\n  }\n\n  public VarType getVarType() {\n    VarType vt = null;\n    if (processor != null) {\n      vt = processor.getVarType(getVarVersionPair());\n    }\n\n    if (vt == null || (varType != null && varType.getType() != CodeConstants.TYPE_UNKNOWN)) {\n      vt = varType;\n    }\n\n    return vt == null ? VarType.VARTYPE_UNKNOWN : vt;\n  }\n\n  public void setVarType(VarType varType) {\n    this.varType = varType;\n  }\n\n  public boolean isDefinition() {\n    return definition;\n  }\n\n  public void setDefinition(boolean definition) {\n    this.definition = definition;\n  }\n\n  public VarProcessor getProcessor() {\n    return processor;\n  }\n\n  public int getVersion() {\n    return version;\n  }\n\n  public void setVersion(int version) {\n    this.version = version;\n  }\n\n  public boolean isClassDef() {\n    return classDef;\n  }\n\n  public void setClassDef(boolean classDef) {\n    this.classDef = classDef;\n  }\n\n  public boolean isStack() {\n    return stack;\n  }\n\n  public void setStack(boolean stack) {\n    this.stack = stack;\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (!super.match(matchNode, engine)) {\n      return false;\n    }\n\n    RuleValue rule = matchNode.getRules().get(MatchProperties.EXPRENT_VAR_INDEX);\n    if (rule != null) {\n      if (rule.isVariable()) {\n        return engine.checkAndSetVariableValue((String)rule.value, this.index);\n      }\n      else {\n        return this.index == Integer.parseInt((String)rule.value);\n      }\n    }\n\n    return true;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.sforms;\n\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.List;\n\n\npublic class DirectGraph {\n\n  public final VBStyleCollection<DirectNode, String> nodes = new VBStyleCollection<>();\n\n  public DirectNode first;\n\n  // exit, [source, destination]\n  public final HashMap<String, List<FinallyPathWrapper>> mapShortRangeFinallyPaths = new HashMap<>();\n\n  // exit, [source, destination]\n  public final HashMap<String, List<FinallyPathWrapper>> mapLongRangeFinallyPaths = new HashMap<>();\n\n  // negative if branches (recorded for handling of && and ||)\n  public final HashMap<String, String> mapNegIfBranch = new HashMap<>();\n\n  // nodes, that are exception exits of a finally block with monitor variable\n  public final HashMap<String, String> mapFinallyMonitorExceptionPathExits = new HashMap<>();\n\n  public void sortReversePostOrder() {\n    LinkedList<DirectNode> res = new LinkedList<>();\n    addToReversePostOrderListIterative(first, res);\n\n    nodes.clear();\n    for (DirectNode node : res) {\n      nodes.addWithKey(node, node.id);\n    }\n  }\n\n  private static void addToReversePostOrderListIterative(DirectNode root, List<? super DirectNode> lst) {\n\n    LinkedList<DirectNode> stackNode = new LinkedList<>();\n    LinkedList<Integer> stackIndex = new LinkedList<>();\n\n    HashSet<DirectNode> setVisited = new HashSet<>();\n\n    stackNode.add(root);\n    stackIndex.add(0);\n\n    while (!stackNode.isEmpty()) {\n\n      DirectNode node = stackNode.getLast();\n      int index = stackIndex.removeLast();\n\n      setVisited.add(node);\n\n      for (; index < node.successors.size(); index++) {\n        DirectNode succ = node.successors.get(index);\n\n        if (!setVisited.contains(succ)) {\n          stackIndex.add(index + 1);\n\n          stackNode.add(succ);\n          stackIndex.add(0);\n\n          break;\n        }\n      }\n\n      if (index == node.successors.size()) {\n        lst.add(0, node);\n\n        stackNode.removeLast();\n      }\n    }\n  }\n\n\n  public boolean iterateExprents(ExprentIterator iter) {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n\n    LinkedList<DirectNode> stack = new LinkedList<>();\n    stack.add(first);\n\n    HashSet<DirectNode> setVisited = new HashSet<>();\n\n    while (!stack.isEmpty()) {\n\n      DirectNode node = stack.removeFirst();\n\n      if (setVisited.contains(node)) {\n        continue;\n      }\n      setVisited.add(node);\n\n      for (int i = 0; i < node.exprents.size(); i++) {\n        cancellationManager.checkCanceled();\n        int res = iter.processExprent(node.exprents.get(i));\n\n        if (res == 1) {\n          return false;\n        }\n\n        if (res == 2) {\n          node.exprents.remove(i);\n          i--;\n        }\n      }\n\n      stack.addAll(node.successors);\n    }\n\n    return true;\n  }\n\n  public interface ExprentIterator {\n    // 0 - success, do nothing\n    // 1 - cancel iteration\n    // 2 - success, delete exprent\n    int processExprent(Exprent exprent);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.sforms;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\npublic class DirectNode {\n  public final @NotNull DirectNodeType type;\n  public final @NotNull String id;\n  public final @NotNull Statement statement;\n  public final @Nullable BasicBlockStatement block;\n  public final List<DirectNode> successors = new ArrayList<>();\n  public final List<DirectNode> predecessors = new ArrayList<>();\n  public List<Exprent> exprents = new ArrayList<>();\n\n  public DirectNode(@NotNull DirectNodeType type, @NotNull Statement statement, @NotNull String id) {\n    this.type = type;\n    this.statement = statement;\n    this.id = id;\n    this.block = null;\n  }\n\n  public DirectNode(@NotNull DirectNodeType type, @NotNull Statement statement, @NotNull BasicBlockStatement block) {\n    this.type = type;\n    this.statement = statement;\n    this.id = Integer.toString(block.id);\n    this.block = block;\n  }\n\n  @Override\n  public String toString() {\n    return id;\n  }\n\n  public enum DirectNodeType {\n    DIRECT,\n    TAIL,\n    INIT,\n    CONDITION,\n    INCREMENT,\n    TRY\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.sforms;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode.DirectNodeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\n\npublic class FlattenStatementsHelper {\n\n  // statement.id, node.id(direct), node.id(continue)\n  private final Map<Integer, String[]> mapDestinationNodes = new HashMap<>();\n\n  // node.id(source), statement.id(destination), edge type\n  private final List<Edge> listEdges = new ArrayList<>();\n\n  // node.id(exit), [node.id(source), statement.id(destination)]\n  private final Map<String, List<String[]>> mapShortRangeFinallyPathIds = new HashMap<>();\n\n  // node.id(exit), [node.id(source), statement.id(destination)]\n  private final Map<String, List<String[]>> mapLongRangeFinallyPathIds = new HashMap<>();\n\n  // positive if branches\n  private final Map<String, Integer> mapPosIfBranch = new HashMap<>();\n\n  private DirectGraph graph;\n\n  private RootStatement root;\n\n  public DirectGraph buildDirectGraph(RootStatement root) {\n\n    this.root = root;\n\n    graph = new DirectGraph();\n\n    flattenStatement();\n\n    // dummy exit node\n    Statement dummyexit = root.getDummyExit();\n    DirectNode node = new DirectNode(DirectNodeType.DIRECT, dummyexit, Integer.toString(dummyexit.id));\n    node.exprents = new ArrayList<>();\n    graph.nodes.addWithKey(node, node.id);\n    mapDestinationNodes.put(dummyexit.id, new String[]{node.id, null});\n\n    setEdges();\n\n    graph.first = graph.nodes.getWithKey(mapDestinationNodes.get(root.id)[0]);\n    graph.sortReversePostOrder();\n\n    return graph;\n  }\n\n  private void flattenStatement() {\n\n    class StatementStackEntry {\n      public final Statement statement;\n      public final LinkedList<StackEntry> stackFinally;\n      public final List<Exprent> tailExprents;\n\n      public int statementIndex;\n      public int edgeIndex;\n      public List<StatEdge> succEdges;\n\n      StatementStackEntry(Statement statement, LinkedList<StackEntry> stackFinally, List<Exprent> tailExprents) {\n        this.statement = statement;\n        this.stackFinally = stackFinally;\n        this.tailExprents = tailExprents;\n      }\n    }\n\n    LinkedList<StatementStackEntry> lstStackStatements = new LinkedList<>();\n\n    lstStackStatements.add(new StatementStackEntry(root, new LinkedList<>(), null));\n\n    mainloop:\n    while (!lstStackStatements.isEmpty()) {\n\n      StatementStackEntry statEntry = lstStackStatements.removeFirst();\n\n      Statement stat = statEntry.statement;\n      LinkedList<StackEntry> stackFinally = statEntry.stackFinally;\n      int statementBreakIndex = statEntry.statementIndex;\n\n      DirectNode node, nd;\n\n      List<StatEdge> lstSuccEdges = new ArrayList<>();\n      DirectNode sourcenode = null;\n\n      if (statEntry.succEdges == null) {\n\n        switch (stat.type) {\n          case BASIC_BLOCK -> {\n            node = new DirectNode(DirectNodeType.DIRECT, stat, (BasicBlockStatement)stat);\n            if (stat.getExprents() != null) {\n              node.exprents = stat.getExprents();\n            }\n            graph.nodes.putWithKey(node, node.id);\n            mapDestinationNodes.put(stat.id, new String[]{node.id, null});\n\n            lstSuccEdges.addAll(stat.getSuccessorEdges(EdgeType.DIRECT_ALL));\n            sourcenode = node;\n\n            List<Exprent> tailExprentList = statEntry.tailExprents;\n\n            if (tailExprentList != null) {\n              DirectNode tail = new DirectNode(DirectNodeType.TAIL, stat, stat.id + \"_tail\");\n              tail.exprents = tailExprentList;\n              graph.nodes.putWithKey(tail, tail.id);\n\n              mapDestinationNodes.put(-stat.id, new String[]{tail.id, null});\n              listEdges.add(new Edge(node.id, -stat.id, EdgeType.REGULAR));\n\n              sourcenode = tail;\n            }\n\n            // 'if' statement: record positive branch\n            if (stat.getLastBasicType() == StatementType.IF) {\n              mapPosIfBranch.put(sourcenode.id, lstSuccEdges.get(0).getDestination().id);\n            }\n          }\n          case CATCH_ALL, TRY_CATCH -> {\n            DirectNode firstnd = new DirectNode(DirectNodeType.TRY, stat, stat.id + \"_try\");\n\n            mapDestinationNodes.put(stat.id, new String[]{firstnd.id, null});\n            graph.nodes.putWithKey(firstnd, firstnd.id);\n\n            LinkedList<StatementStackEntry> lst = new LinkedList<>();\n\n            for (Statement st : stat.getStats()) {\n              listEdges.add(new Edge(firstnd.id, st.id, EdgeType.REGULAR));\n\n              LinkedList<StackEntry> stack = stackFinally;\n              if (stat.type == StatementType.CATCH_ALL && ((CatchAllStatement)stat).isFinally()) {\n                stack = new LinkedList<>(stackFinally);\n\n                if (st == stat.getFirst()) { // catch head\n                  stack.add(new StackEntry((CatchAllStatement)stat, Boolean.FALSE));\n                }\n                else { // handler\n                  stack.add(new StackEntry((CatchAllStatement)stat, Boolean.TRUE, EdgeType.BREAK,\n                                           root.getDummyExit(), st, st, firstnd, firstnd, true));\n                }\n              }\n              lst.add(new StatementStackEntry(st, stack, null));\n            }\n\n            lstStackStatements.addAll(0, lst);\n          }\n          case DO -> {\n            if (statementBreakIndex == 0) {\n              statEntry.statementIndex = 1;\n              lstStackStatements.addFirst(statEntry);\n              lstStackStatements.addFirst(new StatementStackEntry(stat.getFirst(), stackFinally, null));\n\n              continue mainloop;\n            }\n\n            nd = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]);\n\n            DoStatement dostat = (DoStatement)stat;\n            LoopType loopType = dostat.getLoopType();\n\n            if (loopType == LoopType.DO) {\n              mapDestinationNodes.put(stat.id, new String[]{nd.id, nd.id});\n              break;\n            }\n\n            lstSuccEdges.add(stat.getSuccessorEdges(EdgeType.DIRECT_ALL).get(0));  // exactly one edge\n\n            switch (loopType) {\n              case WHILE, DO_WHILE -> {\n                node = new DirectNode(DirectNodeType.CONDITION, stat, stat.id + \"_cond\");\n                node.exprents = dostat.getConditionExprentList();\n                graph.nodes.putWithKey(node, node.id);\n\n                listEdges.add(new Edge(node.id, stat.getFirst().id, EdgeType.REGULAR));\n\n                if (loopType == LoopType.WHILE) {\n                  mapDestinationNodes.put(stat.id, new String[]{node.id, node.id});\n                }\n                else {\n                  mapDestinationNodes.put(stat.id, new String[]{nd.id, node.id});\n\n                  boolean found = false;\n                  for (Edge edge : listEdges) {\n                    if (edge.statid.equals(stat.id) && edge.edgetype == EdgeType.CONTINUE) {\n                      found = true;\n                      break;\n                    }\n                  }\n                  if (!found) {\n                    listEdges.add(new Edge(nd.id, stat.id, EdgeType.CONTINUE));\n                  }\n                }\n                sourcenode = node;\n              }\n              case FOR -> {\n                DirectNode nodeinit = new DirectNode(DirectNodeType.INIT, stat, stat.id + \"_init\");\n                if (dostat.getInitExprent() != null) {\n                  nodeinit.exprents = dostat.getInitExprentList();\n                }\n                graph.nodes.putWithKey(nodeinit, nodeinit.id);\n\n                DirectNode nodecond = new DirectNode(DirectNodeType.CONDITION, stat, stat.id + \"_cond\");\n                nodecond.exprents = dostat.getConditionExprentList();\n                graph.nodes.putWithKey(nodecond, nodecond.id);\n\n                DirectNode nodeinc = new DirectNode(DirectNodeType.INCREMENT, stat, stat.id + \"_inc\");\n                nodeinc.exprents = dostat.getIncExprentList();\n                graph.nodes.putWithKey(nodeinc, nodeinc.id);\n\n                mapDestinationNodes.put(stat.id, new String[]{nodeinit.id, nodeinc.id});\n                mapDestinationNodes.put(-stat.id, new String[]{nodecond.id, null});\n\n                listEdges.add(new Edge(nodecond.id, stat.getFirst().id, EdgeType.REGULAR));\n                listEdges.add(new Edge(nodeinit.id, -stat.id, EdgeType.REGULAR));\n                listEdges.add(new Edge(nodeinc.id, -stat.id, EdgeType.REGULAR));\n\n                boolean found = false;\n                for (Edge edge : listEdges) {\n                  if (edge.statid.equals(stat.id) && edge.edgetype == EdgeType.CONTINUE) {\n                    found = true;\n                    break;\n                  }\n                }\n                if (!found) {\n                  listEdges.add(new Edge(nd.id, stat.id, EdgeType.CONTINUE));\n                }\n\n                sourcenode = nodecond;\n              }\n            }\n          }\n          case SYNCHRONIZED, SWITCH, IF, SEQUENCE, ROOT -> {\n            int statsize = stat.getStats().size();\n            if (stat.type == StatementType.SYNCHRONIZED) {\n              statsize = 2;  // exclude the handler if synchronized\n            }\n\n            if (statementBreakIndex <= statsize) {\n              List<Exprent> tailexprlst = switch (stat.type) {\n                case SYNCHRONIZED -> ((SynchronizedStatement)stat).getHeadexprentList();\n                case SWITCH -> ((SwitchStatement)stat).getHeadExprentList();\n                case IF -> ((IfStatement)stat).getHeadexprentList();\n                default -> null;\n              };\n\n              for (int i = statementBreakIndex; i < statsize; i++) {\n                statEntry.statementIndex = i + 1;\n                lstStackStatements.addFirst(statEntry);\n                lstStackStatements.addFirst(\n                  new StatementStackEntry(stat.getStats().get(i), stackFinally,\n                                          (i == 0 && tailexprlst != null && tailexprlst.get(0) != null) ? tailexprlst : null));\n\n                continue mainloop;\n              }\n\n              node = graph.nodes.getWithKey(mapDestinationNodes.get(stat.getFirst().id)[0]);\n              mapDestinationNodes.put(stat.id, new String[]{node.id, null});\n\n              if (stat.type == StatementType.IF && ((IfStatement)stat).iftype == IfStatement.IFTYPE_IF) {\n                lstSuccEdges.add(stat.getSuccessorEdges(EdgeType.DIRECT_ALL).get(0));  // exactly one edge\n                sourcenode = tailexprlst.get(0) == null ? node : graph.nodes.getWithKey(node.id + \"_tail\");\n              }\n            }\n          }\n        }\n      }\n\n      // no successor edges\n      if (sourcenode != null) {\n\n        if (statEntry.succEdges != null) {\n          lstSuccEdges = statEntry.succEdges;\n        }\n\n        for (int edgeindex = statEntry.edgeIndex; edgeindex < lstSuccEdges.size(); edgeindex++) {\n\n          StatEdge edge = lstSuccEdges.get(edgeindex);\n\n          LinkedList<StackEntry> stack = new LinkedList<>(stackFinally);\n\n          EdgeType edgetype = edge.getType();\n          Statement destination = edge.getDestination();\n\n          DirectNode finallyShortRangeSource = sourcenode;\n          DirectNode finallyLongRangeSource = sourcenode;\n          Statement finallyShortRangeEntry = null;\n          Statement finallyLongRangeEntry = null;\n\n          boolean isFinallyMonitorExceptionPath = false;\n\n          boolean isFinallyExit = false;\n\n          while (true) {\n\n            StackEntry entry = null;\n            if (!stack.isEmpty()) {\n              entry = stack.getLast();\n            }\n\n            boolean created = true;\n\n            if (entry == null) {\n              saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,\n                       finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);\n            }\n            else {\n\n              CatchAllStatement catchall = entry.catchstatement;\n\n              if (entry.state) { // finally handler statement\n                if (edgetype == EdgeType.FINALLY_EXIT) {\n\n                  stack.removeLast();\n                  destination = entry.destination;\n                  edgetype = entry.edgetype;\n\n                  finallyShortRangeSource = entry.finallyShortRangeSource;\n                  finallyLongRangeSource = entry.finallyLongRangeSource;\n                  finallyShortRangeEntry = entry.finallyShortRangeEntry;\n                  finallyLongRangeEntry = entry.finallyLongRangeEntry;\n\n                  isFinallyExit = true;\n                  isFinallyMonitorExceptionPath = (catchall.getMonitor() != null) & entry.isFinallyExceptionPath;\n\n                  created = false;\n                }\n                else {\n                  if (!catchall.containsStatementStrict(destination)) {\n                    stack.removeLast();\n                    created = false;\n                  }\n                  else {\n                    saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,\n                             finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);\n                  }\n                }\n              }\n              else { // finally protected try statement\n                if (!catchall.containsStatementStrict(destination)) {\n                  saveEdge(sourcenode, catchall.getHandler(), EdgeType.REGULAR, isFinallyExit ? finallyShortRangeSource : null,\n                           finallyLongRangeSource, finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);\n\n                  stack.removeLast();\n                  stack.add(new StackEntry(catchall, Boolean.TRUE, edgetype, destination, catchall.getHandler(),\n                                           finallyLongRangeEntry == null ? catchall.getHandler() : finallyLongRangeEntry,\n                                           sourcenode, finallyLongRangeSource, false));\n\n                  statEntry.edgeIndex = edgeindex + 1;\n                  statEntry.succEdges = lstSuccEdges;\n                  lstStackStatements.addFirst(statEntry);\n                  lstStackStatements.addFirst(new StatementStackEntry(catchall.getHandler(), stack, null));\n\n                  continue mainloop;\n                }\n                else {\n                  saveEdge(sourcenode, destination, edgetype, isFinallyExit ? finallyShortRangeSource : null, finallyLongRangeSource,\n                           finallyShortRangeEntry, finallyLongRangeEntry, isFinallyMonitorExceptionPath);\n                }\n              }\n            }\n\n            if (created) {\n              break;\n            }\n          }\n        }\n      }\n    }\n  }\n\n  private void saveEdge(DirectNode sourcenode,\n                        Statement destination,\n                        EdgeType edgetype,\n                        DirectNode finallyShortRangeSource,\n                        DirectNode finallyLongRangeSource,\n                        Statement finallyShortRangeEntry,\n                        Statement finallyLongRangeEntry,\n                        boolean isFinallyMonitorExceptionPath) {\n\n    if (edgetype != EdgeType.FINALLY_EXIT) {\n      listEdges.add(new Edge(sourcenode.id, destination.id, edgetype));\n    }\n\n    if (finallyShortRangeSource != null) {\n      boolean isContinueEdge = (edgetype == EdgeType.CONTINUE);\n\n      mapShortRangeFinallyPathIds.computeIfAbsent(sourcenode.id, k -> new ArrayList<>()).add(new String[]{\n        finallyShortRangeSource.id,\n        Integer.toString(destination.id),\n        Integer.toString(finallyShortRangeEntry.id),\n        isFinallyMonitorExceptionPath ? \"1\" : null,\n        isContinueEdge ? \"1\" : null});\n\n      mapLongRangeFinallyPathIds.computeIfAbsent(sourcenode.id, k -> new ArrayList<>()).add(new String[]{\n        finallyLongRangeSource.id,\n        Integer.toString(destination.id),\n        Integer.toString(finallyLongRangeEntry.id),\n        isContinueEdge ? \"1\" : null});\n    }\n  }\n\n  private void setEdges() {\n\n    for (Edge edge : listEdges) {\n\n      String sourceid = edge.sourceid;\n      Integer statid = edge.statid;\n\n      DirectNode source = graph.nodes.getWithKey(sourceid);\n\n      DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(statid)[edge.edgetype == EdgeType.CONTINUE ? 1 : 0]);\n\n      if (!source.successors.contains(dest)) {\n        source.successors.add(dest);\n      }\n\n      if (!dest.predecessors.contains(source)) {\n        dest.predecessors.add(source);\n      }\n\n      if (mapPosIfBranch.containsKey(sourceid) && !statid.equals(mapPosIfBranch.get(sourceid))) {\n        graph.mapNegIfBranch.put(sourceid, dest.id);\n      }\n    }\n\n    for (int i = 0; i < 2; i++) {\n      for (Entry<String, List<String[]>> ent : (i == 0 ? mapShortRangeFinallyPathIds : mapLongRangeFinallyPathIds).entrySet()) {\n\n        List<FinallyPathWrapper> newLst = new ArrayList<>();\n\n        List<String[]> lst = ent.getValue();\n        for (String[] arr : lst) {\n\n          boolean isContinueEdge = arr[i == 0 ? 4 : 3] != null;\n\n          DirectNode dest = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[1]))[isContinueEdge ? 1 : 0]);\n          DirectNode enter = graph.nodes.getWithKey(mapDestinationNodes.get(Integer.parseInt(arr[2]))[0]);\n\n          newLst.add(new FinallyPathWrapper(arr[0], dest.id, enter.id));\n\n          if (i == 0 && arr[3] != null) {\n            graph.mapFinallyMonitorExceptionPathExits.put(ent.getKey(), dest.id);\n          }\n        }\n\n        if (!newLst.isEmpty()) {\n          (i == 0 ? graph.mapShortRangeFinallyPaths : graph.mapLongRangeFinallyPaths).put(ent.getKey(),\n                                                                                          new ArrayList<>(\n                                                                                            new HashSet<>(newLst)));\n        }\n      }\n    }\n  }\n\n  public Map<Integer, String[]> getMapDestinationNodes() {\n    return mapDestinationNodes;\n  }\n\n  public static final class FinallyPathWrapper {\n    public final String source;\n    public final String destination;\n    public final String entry;\n\n    private FinallyPathWrapper(String source, String destination, String entry) {\n      this.source = source;\n      this.destination = destination;\n      this.entry = entry;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n      if (o == this) return true;\n      if (!(o instanceof FinallyPathWrapper fpw)) return false;\n\n      return (source + \":\" + destination + \":\" + entry).equals(fpw.source + \":\" + fpw.destination + \":\" + fpw.entry);\n    }\n\n    @Override\n    public int hashCode() {\n      return (source + \":\" + destination + \":\" + entry).hashCode();\n    }\n\n    @Override\n    public String toString() {\n      return source + \"->(\" + entry + \")->\" + destination;\n    }\n  }\n\n\n  private static class StackEntry {\n\n    public final CatchAllStatement catchstatement;\n    public final boolean state;\n    public final EdgeType edgetype;\n    public final boolean isFinallyExceptionPath;\n\n    public final Statement destination;\n    public final Statement finallyShortRangeEntry;\n    public final Statement finallyLongRangeEntry;\n    public final DirectNode finallyShortRangeSource;\n    public final DirectNode finallyLongRangeSource;\n\n    StackEntry(CatchAllStatement catchstatement,\n               boolean state,\n               EdgeType edgetype,\n               Statement destination,\n               Statement finallyShortRangeEntry,\n               Statement finallyLongRangeEntry,\n               DirectNode finallyShortRangeSource,\n               DirectNode finallyLongRangeSource,\n               boolean isFinallyExceptionPath) {\n\n      this.catchstatement = catchstatement;\n      this.state = state;\n      this.edgetype = edgetype;\n      this.isFinallyExceptionPath = isFinallyExceptionPath;\n\n      this.destination = destination;\n      this.finallyShortRangeEntry = finallyShortRangeEntry;\n      this.finallyLongRangeEntry = finallyLongRangeEntry;\n      this.finallyShortRangeSource = finallyShortRangeSource;\n      this.finallyLongRangeSource = finallyLongRangeSource;\n    }\n\n    StackEntry(CatchAllStatement catchstatement, boolean state) {\n      this(catchstatement, state, EdgeType.NULL, null, null, null, null, null, false);\n    }\n  }\n\n  private static class Edge {\n    public final String sourceid;\n    public final Integer statid;\n    public final EdgeType edgetype;\n\n    Edge(String sourceid, Integer statid, EdgeType edgetype) {\n      this.sourceid = sourceid;\n      this.statid = statid;\n      this.edgetype = edgetype;\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.sforms;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.util.FastSparseSetFactory;\nimport org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;\nimport org.jetbrains.java.decompiler.util.SFormsFastMapDirect;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.Objects;\n\npublic class SSAConstructorSparseEx {\n\n  // node id, var, version\n  private final HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<>();\n\n  // node id, var, version (direct branch)\n  private final HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<>();\n\n  // node id, var, version (negative branch)\n  private final HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<>();\n\n  // node id, var, version\n  private final HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<>();\n\n  // (var, version), version\n  private final HashMap<VarVersionPair, FastSparseSet<Integer>> phi = new HashMap<>();\n\n  // var, version\n  private final HashMap<Integer, Integer> lastversion = new HashMap<>();\n\n  // set factory\n  private FastSparseSetFactory<Integer> factory;\n\n  public void splitVariables(RootStatement root, StructMethod mt) {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n\n    FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();\n    DirectGraph dgraph = flatthelper.buildDirectGraph(root);\n\n    // try {\n    // DotExporter.toDotFile(dgraph, new File(\"c:\\\\Temp\\\\gr12_my.dot\"));\n    // } catch(Exception ex) {ex.printStackTrace();}\n\n    HashSet<Integer> setInit = new HashSet<>();\n    for (int i = 0; i < 64; i++) {\n      setInit.add(i);\n    }\n    factory = new FastSparseSetFactory<>(setInit);\n\n    SFormsFastMapDirect firstmap = createFirstMap(mt);\n    extraVarVersions.put(dgraph.first.id, firstmap);\n\n    setCatchMaps(root, dgraph, flatthelper);\n\n    HashSet<String> updated = new HashSet<>();\n    do {\n      // System.out.println(\"~~~~~~~~~~~~~ \\r\\n\"+root.toJava());\n      cancellationManager.checkCanceled();\n      ssaStatements(dgraph, updated);\n      // System.out.println(\"~~~~~~~~~~~~~ \\r\\n\"+root.toJava());\n    }\n    while (!updated.isEmpty());\n  }\n\n  private void ssaStatements(DirectGraph dgraph, HashSet<String> updated) {\n\n    // try {\n    // DotExporter.toDotFile(dgraph, new File(\"c:\\\\Temp\\\\gr1_my.dot\"));\n    // } catch(Exception ex) {ex.printStackTrace();}\n\n    for (DirectNode node : dgraph.nodes) {\n\n      //\t\t\tif (node.id.endsWith(\"_inc\")) {\n      //\t\t\t\tSystem.out.println();\n      //\n      //\t\t\t\ttry {\n      //\t\t\t\t\tDotExporter.toDotFile(dgraph, new File(\"c:\\\\Temp\\\\gr1_my.dot\"));\n      //\t\t\t\t} catch (Exception ex) {\n      //\t\t\t\t\tex.printStackTrace();\n      //\t\t\t\t}\n      //\t\t\t}\n\n      updated.remove(node.id);\n      mergeInVarMaps(node, dgraph);\n\n      SFormsFastMapDirect varmap = inVarVersions.get(node.id);\n      varmap = new SFormsFastMapDirect(varmap);\n\n      SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[]{varmap, null};\n\n      if (node.exprents != null) {\n        for (Exprent expr : node.exprents) {\n          processExprent(expr, varmaparr);\n        }\n      }\n\n      if (varmaparr[1] == null) {\n        varmaparr[1] = varmaparr[0];\n      }\n\n      boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id))\n                             || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id)));\n\n      if (this_updated) {\n        outVarVersions.put(node.id, varmaparr[0]);\n        if (dgraph.mapNegIfBranch.containsKey(node.id)) {\n          outNegVarVersions.put(node.id, varmaparr[1]);\n        }\n\n        for (DirectNode nd : node.successors) {\n          updated.add(nd.id);\n        }\n      }\n    }\n  }\n\n  private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr) {\n\n    if (expr == null) {\n      return;\n    }\n\n    VarExprent varassign = null;\n    boolean finished = false;\n\n    switch (expr.type) {\n      case Exprent.EXPRENT_ASSIGNMENT -> {\n        AssignmentExprent assexpr = (AssignmentExprent)expr;\n        if (assexpr.getCondType() == AssignmentExprent.CONDITION_NONE) {\n          Exprent dest = assexpr.getLeft();\n          if (dest.type == Exprent.EXPRENT_VAR) {\n            varassign = (VarExprent)dest;\n          }\n        }\n      }\n      case Exprent.EXPRENT_FUNCTION -> {\n        FunctionExprent func = (FunctionExprent)expr;\n        switch (func.getFuncType()) {\n          case FunctionExprent.FUNCTION_IIF -> {\n            processExprent(func.getLstOperands().get(0), varmaparr);\n\n            SFormsFastMapDirect varmapFalse;\n            if (varmaparr[1] == null) {\n              varmapFalse = new SFormsFastMapDirect(varmaparr[0]);\n            }\n            else {\n              varmapFalse = varmaparr[1];\n              varmaparr[1] = null;\n            }\n\n            processExprent(func.getLstOperands().get(1), varmaparr);\n\n            SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[]{varmapFalse, null};\n            processExprent(func.getLstOperands().get(2), varmaparrNeg);\n\n            mergeMaps(varmaparr[0], varmaparrNeg[0]);\n            varmaparr[1] = null;\n\n            finished = true;\n          }\n          case FunctionExprent.FUNCTION_CADD -> {\n            processExprent(func.getLstOperands().get(0), varmaparr);\n\n            SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[0]), null};\n\n            processExprent(func.getLstOperands().get(1), varmaparrAnd);\n\n            // false map\n            varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]);\n            // true map\n            varmaparr[0] = varmaparrAnd[0];\n\n            finished = true;\n          }\n          case FunctionExprent.FUNCTION_COR -> {\n            processExprent(func.getLstOperands().get(0), varmaparr);\n\n            SFormsFastMapDirect[] varmaparrOr =\n              new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null};\n\n            processExprent(func.getLstOperands().get(1), varmaparrOr);\n\n            // false map\n            varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1];\n            // true map\n            varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]);\n\n            finished = true;\n          }\n        }\n      }\n    }\n\n    if (finished) {\n      return;\n    }\n\n    List<Exprent> lst = expr.getAllExprents();\n    lst.remove(varassign);\n\n    for (Exprent ex : lst) {\n      processExprent(ex, varmaparr);\n    }\n\n    SFormsFastMapDirect varmap = varmaparr[0];\n\n    if (varassign != null) {\n\n      Integer varindex = varassign.getIndex();\n\n      if (varassign.getVersion() == 0) {\n        // get next version\n        Integer nextver = getNextFreeVersion(varindex);\n\n        // set version\n        varassign.setVersion(nextver);\n\n        setCurrentVar(varmap, varindex, nextver);\n      }\n      else {\n        setCurrentVar(varmap, varindex, varassign.getVersion());\n      }\n    }\n    else if (expr.type == Exprent.EXPRENT_VAR) {\n\n      VarExprent vardest = (VarExprent)expr;\n      Integer varindex = vardest.getIndex();\n      FastSparseSet<Integer> vers = varmap.get(varindex);\n\n      int cardinality = vers.getCardinality();\n      if (cardinality == 1) { // == 1\n        // set version\n        Integer it = vers.iterator().next();\n        vardest.setVersion(it);\n      }\n      else if (cardinality == 2) { // size > 1\n        Integer current_vers = vardest.getVersion();\n\n        VarVersionPair currpaar = new VarVersionPair(varindex, current_vers);\n        if (current_vers != 0 && phi.containsKey(currpaar)) {\n          setCurrentVar(varmap, varindex, current_vers);\n          // update phi node\n          phi.get(currpaar).union(vers);\n        }\n        else {\n          // increase version\n          Integer nextver = getNextFreeVersion(varindex);\n          // set version\n          vardest.setVersion(nextver);\n\n          setCurrentVar(varmap, varindex, nextver);\n          // create new phi node\n          phi.put(new VarVersionPair(varindex, nextver), vers);\n        }\n      } // 0 means uninitialized variable, which is impossible\n    }\n  }\n\n  private Integer getNextFreeVersion(Integer var) {\n    Integer nextver = lastversion.get(var);\n    if (nextver == null) {\n      nextver = 1;\n    }\n    else {\n      nextver++;\n    }\n    lastversion.put(var, nextver);\n    return nextver;\n  }\n\n  private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) {\n\n    SFormsFastMapDirect mapNew = new SFormsFastMapDirect();\n\n    for (DirectNode pred : node.predecessors) {\n      SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id);\n      if (mapNew.isEmpty()) {\n        mapNew = mapOut.getCopy();\n      }\n      else {\n        mergeMaps(mapNew, mapOut);\n      }\n    }\n\n    if (extraVarVersions.containsKey(node.id)) {\n      SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id);\n      if (mapNew.isEmpty()) {\n        mapNew = mapExtra.getCopy();\n      }\n      else {\n        mergeMaps(mapNew, mapExtra);\n      }\n    }\n\n    inVarVersions.put(node.id, mapNew);\n  }\n\n  private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) {\n\n    SFormsFastMapDirect mapNew = new SFormsFastMapDirect();\n\n    if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) {\n      if (outNegVarVersions.containsKey(predid)) {\n        mapNew = outNegVarVersions.get(predid).getCopy();\n      }\n    }\n    else if (outVarVersions.containsKey(predid)) {\n      mapNew = outVarVersions.get(predid).getCopy();\n    }\n\n    boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid);\n\n    if (isFinallyExit && !mapNew.isEmpty()) {\n\n      SFormsFastMapDirect mapNewTemp = mapNew.getCopy();\n\n      SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect();\n\n      String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid);\n      boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest));\n\n      HashSet<String> setLongPathWrapper = new HashSet<>();\n      for (FinallyPathWrapper finwraplong : dgraph.mapLongRangeFinallyPaths.get(predid)) {\n        setLongPathWrapper.add(finwraplong.destination + \"##\" + finwraplong.source);\n      }\n\n      for (FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) {\n        SFormsFastMapDirect map;\n\n        boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source);\n\n        if (recFinally) {\n          // recursion\n          map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid);\n        }\n        else {\n          if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) {\n            map = outNegVarVersions.get(finwrap.source);\n          }\n          else {\n            map = outVarVersions.get(finwrap.source);\n          }\n        }\n\n        // false path?\n        boolean isFalsePath;\n\n        if (recFinally) {\n          isFalsePath = !finwrap.destination.equals(nodeid);\n        }\n        else {\n          isFalsePath = !setLongPathWrapper.contains(destid + \"##\" + finwrap.source);\n        }\n\n        if (isFalsePath) {\n          mapNewTemp.complement(map);\n        }\n        else {\n          if (mapTrueSource.isEmpty()) {\n            if (map != null) {\n              mapTrueSource = map.getCopy();\n            }\n          }\n          else {\n            mergeMaps(mapTrueSource, map);\n          }\n        }\n      }\n\n      if (isExceptionMonitorExit) {\n\n        mapNew = mapTrueSource;\n      }\n      else {\n\n        mapNewTemp.union(mapTrueSource);\n\n        SFormsFastMapDirect oldInMap = inVarVersions.get(nodeid);\n        if (oldInMap != null) {\n          mapNewTemp.union(oldInMap);\n        }\n\n        mapNew.intersection(mapNewTemp);\n      }\n    }\n\n    return mapNew;\n  }\n\n  private static SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) {\n\n    if (map2 != null && !map2.isEmpty()) {\n      mapTo.union(map2);\n    }\n\n    return mapTo;\n  }\n\n  private static boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) {\n\n    if (map1 == null) {\n      return map2 == null;\n    }\n    else if (map2 == null) {\n      return false;\n    }\n\n    if (map1.size() != map2.size()) {\n      return false;\n    }\n\n    for (Entry<Integer, FastSparseSet<Integer>> ent2 : map2.entryList()) {\n      if (!Objects.equals(map1.get(ent2.getKey()), ent2.getValue())) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) {\n    FastSparseSet<Integer> set = factory.spawnEmptySet();\n    set.add(vers);\n    varmap.put(var, set);\n  }\n\n  private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) {\n\n    SFormsFastMapDirect map;\n\n    switch (stat.type) {\n      case CATCH_ALL, TRY_CATCH -> {\n        List<VarExprent> lstVars;\n        if (stat.type == StatementType.CATCH_ALL) {\n          lstVars = ((CatchAllStatement)stat).getVars();\n        }\n        else {\n          lstVars = ((CatchStatement)stat).getVars();\n        }\n\n        for (int i = 1; i < stat.getStats().size(); i++) {\n          int varindex = lstVars.get(i - 1).getIndex();\n          int version = getNextFreeVersion(varindex); // == 1\n\n          map = new SFormsFastMapDirect();\n          setCurrentVar(map, varindex, version);\n\n          extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map);\n        }\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      setCatchMaps(st, dgraph, flatthelper);\n    }\n  }\n\n  private SFormsFastMapDirect createFirstMap(StructMethod mt) {\n    boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);\n\n    MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n\n    int paramcount = md.params.length + (thisvar ? 1 : 0);\n\n    int varindex = 0;\n    SFormsFastMapDirect map = new SFormsFastMapDirect();\n    for (int i = 0; i < paramcount; i++) {\n      int version = getNextFreeVersion(varindex); // == 1\n\n      FastSparseSet<Integer> set = factory.spawnEmptySet();\n      set.add(version);\n      map.put(varindex, set);\n\n      if (thisvar) {\n        if (i == 0) {\n          varindex++;\n        }\n        else {\n          varindex += md.params[i - 1].getStackSize();\n        }\n      }\n      else {\n        varindex += md.params[i].getStackSize();\n      }\n    }\n\n    return map;\n  }\n\n  public HashMap<VarVersionPair, FastSparseSet<Integer>> getPhi() {\n    return phi;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.sforms;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper.FinallyPathWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionNode;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionsGraph;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.util.FastSparseSetFactory;\nimport org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;\nimport org.jetbrains.java.decompiler.util.SFormsFastMapDirect;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class SSAUConstructorSparseEx {\n\n  // node id, var, version\n  private final HashMap<String, SFormsFastMapDirect> inVarVersions = new HashMap<>();\n  //private HashMap<String, HashMap<Integer, FastSet<Integer>>> inVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>();\n\n  // node id, var, version (direct branch)\n  private final HashMap<String, SFormsFastMapDirect> outVarVersions = new HashMap<>();\n  //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>();\n\n  // node id, var, version (negative branch)\n  private final HashMap<String, SFormsFastMapDirect> outNegVarVersions = new HashMap<>();\n  //private HashMap<String, HashMap<Integer, FastSet<Integer>>> outNegVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>();\n\n  // node id, var, version\n  private final HashMap<String, SFormsFastMapDirect> extraVarVersions = new HashMap<>();\n  //private HashMap<String, HashMap<Integer, FastSet<Integer>>> extraVarVersions = new HashMap<String, HashMap<Integer, FastSet<Integer>>>();\n\n  // var, version\n  private final HashMap<Integer, Integer> lastversion = new HashMap<>();\n\n  // version, protected ranges (catch, finally)\n  private final HashMap<VarVersionPair, Integer> mapVersionFirstRange = new HashMap<>();\n\n  // version, version\n  private final HashMap<VarVersionPair, VarVersionPair> phantomppnodes = new HashMap<>(); // ++ and --\n\n  // node.id, version, version\n  private final HashMap<String, HashMap<VarVersionPair, VarVersionPair>> phantomexitnodes =\n    new HashMap<>(); // finally exits\n\n  // versions memory dependencies\n  private final VarVersionsGraph ssuversions = new VarVersionsGraph();\n\n  // field access vars (exprent id, var id)\n  private final HashMap<Integer, Integer> mapFieldVars = new HashMap<>();\n\n  // field access counter\n  private int fieldvarcounter = -1;\n\n  // set factory\n  private FastSparseSetFactory<Integer> factory;\n\n  public void splitVariables(RootStatement root, StructMethod mt) {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n\n    FlattenStatementsHelper flatthelper = new FlattenStatementsHelper();\n    DirectGraph dgraph = flatthelper.buildDirectGraph(root);\n\n    HashSet<Integer> setInit = new HashSet<>();\n    for (int i = 0; i < 64; i++) {\n      setInit.add(i);\n    }\n    factory = new FastSparseSetFactory<>(setInit);\n\n    extraVarVersions.put(dgraph.first.id, createFirstMap(mt, root));\n\n    setCatchMaps(root, dgraph, flatthelper);\n\n    //\t\ttry {\n    //\t\t\tDotExporter.toDotFile(dgraph, new File(\"c:\\\\Temp\\\\gr12_my.dot\"));\n    //\t\t} catch(Exception ex) {ex.printStackTrace();}\n\n    HashSet<String> updated = new HashSet<>();\n    do {\n      //\t\t\tSystem.out.println(\"~~~~~~~~~~~~~ \\r\\n\"+root.toJava());\n      ssaStatements(dgraph, updated, false);\n      cancellationManager.checkCanceled();\n      //\t\t\tSystem.out.println(\"~~~~~~~~~~~~~ \\r\\n\"+root.toJava());\n    }\n    while (!updated.isEmpty());\n\n\n    ssaStatements(dgraph, updated, true);\n\n    ssuversions.initDominators();\n  }\n\n  private void ssaStatements(DirectGraph dgraph, HashSet<String> updated, boolean calcLiveVars) {\n    CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n    for (DirectNode node : dgraph.nodes) {\n\n      updated.remove(node.id);\n      mergeInVarMaps(node, dgraph);\n\n      SFormsFastMapDirect varmap = new SFormsFastMapDirect(inVarVersions.get(node.id));\n\n      SFormsFastMapDirect[] varmaparr = new SFormsFastMapDirect[]{varmap, null};\n\n      if (node.exprents != null) {\n        for (Exprent expr : node.exprents) {\n          cancellationManager.checkCanceled();\n          processExprent(expr, varmaparr, node.statement, calcLiveVars);\n        }\n      }\n\n      if (varmaparr[1] == null) {\n        varmaparr[1] = varmaparr[0];\n      }\n\n      // quick solution: 'dummy' field variables should not cross basic block borders (otherwise problems e.g. with finally loops - usage without assignment in a loop)\n      // For the full solution consider adding a dummy assignment at the entry point of the method\n      boolean allow_field_propagation = node.successors.isEmpty() || (node.successors.size() == 1 && node.successors.get(0).predecessors.size() == 1);\n\n      if (!allow_field_propagation && varmaparr[0] != null) {\n        varmaparr[0].removeAllFields();\n        varmaparr[1].removeAllFields();\n      }\n\n      boolean this_updated = !mapsEqual(varmaparr[0], outVarVersions.get(node.id))\n                             || (outNegVarVersions.containsKey(node.id) && !mapsEqual(varmaparr[1], outNegVarVersions.get(node.id)));\n\n      if (this_updated) {\n\n        outVarVersions.put(node.id, varmaparr[0]);\n        if (dgraph.mapNegIfBranch.containsKey(node.id)) {\n          outNegVarVersions.put(node.id, varmaparr[1]);\n        }\n\n        for (DirectNode nd : node.successors) {\n          updated.add(nd.id);\n        }\n      }\n    }\n  }\n\n\n  private void processExprent(Exprent expr, SFormsFastMapDirect[] varmaparr, Statement stat, boolean calcLiveVars) {\n\n    if (expr == null) {\n      return;\n    }\n\n\n    VarExprent varassign = null;\n    boolean finished = false;\n\n    switch (expr.type) {\n      case Exprent.EXPRENT_ASSIGNMENT -> {\n        AssignmentExprent assexpr = (AssignmentExprent)expr;\n        if (assexpr.getCondType() == AssignmentExprent.CONDITION_NONE) {\n          Exprent dest = assexpr.getLeft();\n          if (dest.type == Exprent.EXPRENT_VAR) {\n            varassign = (VarExprent)dest;\n          }\n        }\n      }\n      case Exprent.EXPRENT_FUNCTION -> {\n        FunctionExprent func = (FunctionExprent)expr;\n        switch (func.getFuncType()) {\n          case FunctionExprent.FUNCTION_IIF -> {\n            processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars);\n\n            SFormsFastMapDirect varmapFalse;\n            if (varmaparr[1] == null) {\n              varmapFalse = new SFormsFastMapDirect(varmaparr[0]);\n            }\n            else {\n              varmapFalse = varmaparr[1];\n              varmaparr[1] = null;\n            }\n\n            processExprent(func.getLstOperands().get(1), varmaparr, stat, calcLiveVars);\n\n            SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[]{varmapFalse, null};\n            processExprent(func.getLstOperands().get(2), varmaparrNeg, stat, calcLiveVars);\n\n            mergeMaps(varmaparr[0], varmaparrNeg[0]);\n            varmaparr[1] = null;\n\n            finished = true;\n          }\n          case FunctionExprent.FUNCTION_CADD -> {\n            processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars);\n\n            SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[0]), null};\n\n            processExprent(func.getLstOperands().get(1), varmaparrAnd, stat, calcLiveVars);\n\n            // false map\n            varmaparr[1] = mergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd[1] == null ? 0 : 1]);\n            // true map\n            varmaparr[0] = varmaparrAnd[0];\n\n            finished = true;\n          }\n          case FunctionExprent.FUNCTION_COR -> {\n            processExprent(func.getLstOperands().get(0), varmaparr, stat, calcLiveVars);\n\n            SFormsFastMapDirect[] varmaparrOr =\n              new SFormsFastMapDirect[]{new SFormsFastMapDirect(varmaparr[varmaparr[1] == null ? 0 : 1]), null};\n\n            processExprent(func.getLstOperands().get(1), varmaparrOr, stat, calcLiveVars);\n\n            // false map\n            varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1];\n            // true map\n            varmaparr[0] = mergeMaps(varmaparr[0], varmaparrOr[0]);\n\n            finished = true;\n          }\n        }\n      }\n    }\n\n    if (!finished) {\n      List<Exprent> lst = expr.getAllExprents();\n      lst.remove(varassign);\n\n      for (Exprent ex : lst) {\n        processExprent(ex, varmaparr, stat, calcLiveVars);\n      }\n    }\n\n\n    SFormsFastMapDirect varmap = varmaparr[0];\n\n    // field access\n    if (expr.type == Exprent.EXPRENT_FIELD) {\n\n      int index;\n      if (mapFieldVars.containsKey(expr.id)) {\n        index = mapFieldVars.get(expr.id);\n      }\n      else {\n        index = fieldvarcounter--;\n        mapFieldVars.put(expr.id, index);\n\n        // ssu graph\n        ssuversions.createNode(new VarVersionPair(index, 1));\n      }\n\n      setCurrentVar(varmap, index, 1);\n    }\n    else if (expr.type == Exprent.EXPRENT_INVOCATION ||\n             (expr.type == Exprent.EXPRENT_ASSIGNMENT && ((AssignmentExprent)expr).getLeft().type == Exprent.EXPRENT_FIELD) ||\n             (expr.type == Exprent.EXPRENT_NEW && ((NewExprent)expr).getNewType().getType() == CodeConstants.TYPE_OBJECT) ||\n             expr.type == Exprent.EXPRENT_FUNCTION) {\n\n      boolean ismmpp = true;\n\n      if (expr.type == Exprent.EXPRENT_FUNCTION) {\n\n        ismmpp = false;\n\n        FunctionExprent fexpr = (FunctionExprent)expr;\n        if (fexpr.getFuncType() >= FunctionExprent.FUNCTION_IMM && fexpr.getFuncType() <= FunctionExprent.FUNCTION_PPI) {\n          if (fexpr.getLstOperands().get(0).type == Exprent.EXPRENT_FIELD) {\n            ismmpp = true;\n          }\n        }\n      }\n\n      if (ismmpp) {\n        varmap.removeAllFields();\n      }\n    }\n\n\n    if (varassign != null) {\n\n      Integer varindex = varassign.getIndex();\n\n      if (varassign.getVersion() == 0) {\n        // get next version\n        Integer nextver = getNextFreeVersion(varindex, stat);\n\n        // set version\n        varassign.setVersion(nextver);\n\n        // ssu graph\n        ssuversions.createNode(new VarVersionPair(varindex, nextver));\n\n        setCurrentVar(varmap, varindex, nextver);\n      }\n      else {\n        if (calcLiveVars) {\n          varMapToGraph(new VarVersionPair(varindex.intValue(), varassign.getVersion()), varmap);\n        }\n        setCurrentVar(varmap, varindex, varassign.getVersion());\n      }\n    }\n    else if (expr.type == Exprent.EXPRENT_FUNCTION) { // MM or PP function\n      FunctionExprent func = (FunctionExprent)expr;\n\n      switch (func.getFuncType()) {\n        case FunctionExprent.FUNCTION_IMM, FunctionExprent.FUNCTION_MMI, FunctionExprent.FUNCTION_IPP, FunctionExprent.FUNCTION_PPI -> {\n          if (func.getLstOperands().get(0).type == Exprent.EXPRENT_VAR) {\n            VarExprent var = (VarExprent)func.getLstOperands().get(0);\n            Integer varindex = var.getIndex();\n            VarVersionPair varpaar = new VarVersionPair(varindex.intValue(), var.getVersion());\n\n            // ssu graph\n            VarVersionPair phantomver = phantomppnodes.get(varpaar);\n            if (phantomver == null) {\n              // get next version\n              Integer nextver = getNextFreeVersion(varindex, null);\n              phantomver = new VarVersionPair(varindex, nextver);\n              //ssuversions.createOrGetNode(phantomver);\n              ssuversions.createNode(phantomver);\n\n              VarVersionNode vernode = ssuversions.nodes.getWithKey(varpaar);\n\n              FastSparseSet<Integer> vers = factory.spawnEmptySet();\n              if (vernode.predecessors.size() == 1) {\n                vers.add(vernode.predecessors.iterator().next().source.version);\n              }\n              else {\n                for (VarVersionEdge edge : vernode.predecessors) {\n                  vers.add(edge.source.predecessors.iterator().next().source.version);\n                }\n              }\n              vers.add(nextver);\n              createOrUpdatePhiNode(varpaar, vers, stat);\n              phantomppnodes.put(varpaar, phantomver);\n            }\n            if (calcLiveVars) {\n              varMapToGraph(varpaar, varmap);\n            }\n            setCurrentVar(varmap, varindex, var.getVersion());\n          }\n        }\n      }\n    }\n    else if (expr.type == Exprent.EXPRENT_VAR) {\n\n      VarExprent vardest = (VarExprent)expr;\n\n      Integer varindex = vardest.getIndex();\n      Integer current_vers = vardest.getVersion();\n\n      FastSparseSet<Integer> vers = varmap.get(varindex);\n\n      int cardinality = vers.getCardinality();\n      if (cardinality == 1) { // size == 1\n        if (current_vers != 0) {\n          if (calcLiveVars) {\n            varMapToGraph(new VarVersionPair(varindex, current_vers), varmap);\n          }\n          setCurrentVar(varmap, varindex, current_vers);\n        }\n        else {\n          // split last version\n          Integer usever = getNextFreeVersion(varindex, stat);\n\n          // set version\n          vardest.setVersion(usever);\n          setCurrentVar(varmap, varindex, usever);\n\n          // ssu graph\n          Integer lastver = vers.iterator().next();\n          VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPair(varindex, lastver));\n          VarVersionNode usenode = ssuversions.createNode(new VarVersionPair(varindex, usever));\n          VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, usenode);\n          prenode.addSuccessor(edge);\n          usenode.addPredecessor(edge);\n        }\n      }\n      else if (cardinality == 2) { // size > 1\n\n        if (current_vers != 0) {\n          if (calcLiveVars) {\n            varMapToGraph(new VarVersionPair(varindex, current_vers), varmap);\n          }\n          setCurrentVar(varmap, varindex, current_vers);\n        }\n        else {\n          // split version\n          Integer usever = getNextFreeVersion(varindex, stat);\n          // set version\n          vardest.setVersion(usever);\n\n          // ssu node\n          ssuversions.createNode(new VarVersionPair(varindex, usever));\n\n          setCurrentVar(varmap, varindex, usever);\n\n          current_vers = usever;\n        }\n\n        createOrUpdatePhiNode(new VarVersionPair(varindex, current_vers), vers, stat);\n      } // vers.size() == 0 means uninitialized variable, which is impossible\n    }\n  }\n\n  private void createOrUpdatePhiNode(VarVersionPair phivar, FastSparseSet<Integer> vers, Statement stat) {\n\n    FastSparseSet<Integer> versCopy = vers.getCopy();\n\n    // take into account the corresponding mm/pp node if existing\n    int ppvers = phantomppnodes.containsKey(phivar) ? phantomppnodes.get(phivar).version : -1;\n\n    // ssu graph\n    VarVersionNode phinode = ssuversions.nodes.getWithKey(phivar);\n    List<VarVersionEdge> lstPreds = new ArrayList<>(phinode.predecessors);\n    if (lstPreds.size() == 1) {\n      // not yet a phi node\n      VarVersionEdge edge = lstPreds.get(0);\n      edge.source.removeSuccessor(edge);\n      phinode.removePredecessor(edge);\n    }\n    else {\n      for (VarVersionEdge edge : lstPreds) {\n        int verssrc = edge.source.predecessors.iterator().next().source.version;\n        if (!vers.contains(verssrc) && verssrc != ppvers) {\n          edge.source.removeSuccessor(edge);\n          phinode.removePredecessor(edge);\n        }\n        else {\n          versCopy.remove(verssrc);\n        }\n      }\n    }\n\n    List<VarVersionNode> colnodes = new ArrayList<>();\n    List<VarVersionPair> colpaars = new ArrayList<>();\n\n    for (Integer ver : versCopy) {\n\n      VarVersionNode prenode = ssuversions.nodes.getWithKey(new VarVersionPair(phivar.var, ver.intValue()));\n\n      Integer tempver = getNextFreeVersion(phivar.var, stat);\n\n      VarVersionNode tempnode = new VarVersionNode(phivar.var, tempver);\n\n      colnodes.add(tempnode);\n      colpaars.add(new VarVersionPair(phivar.var, tempver.intValue()));\n\n      VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, prenode, tempnode);\n\n      prenode.addSuccessor(edge);\n      tempnode.addPredecessor(edge);\n\n\n      edge = new VarVersionEdge(VarVersionEdge.EDGE_GENERAL, tempnode, phinode);\n      tempnode.addSuccessor(edge);\n      phinode.addPredecessor(edge);\n    }\n\n    ssuversions.addNodes(colnodes, colpaars);\n  }\n\n  private void varMapToGraph(VarVersionPair varpaar, SFormsFastMapDirect varmap) {\n\n    VBStyleCollection<VarVersionNode, VarVersionPair> nodes = ssuversions.nodes;\n\n    VarVersionNode node = nodes.getWithKey(varpaar);\n\n    node.live = new SFormsFastMapDirect(varmap);\n  }\n\n  private Integer getNextFreeVersion(Integer var, Statement stat) {\n\n    Integer nextver = lastversion.get(var);\n\n    if (nextver == null) {\n      nextver = 1;\n    }\n    else {\n      nextver++;\n    }\n    lastversion.put(var, nextver);\n\n    // save the first protected range, containing current statement\n    if (stat != null) { // null iff phantom version\n      Integer firstRangeId = getFirstProtectedRange(stat);\n      if (firstRangeId != null) {\n        mapVersionFirstRange.put(new VarVersionPair(var, nextver), firstRangeId);\n      }\n    }\n\n    return nextver;\n  }\n\n  private void mergeInVarMaps(DirectNode node, DirectGraph dgraph) {\n\n\n    SFormsFastMapDirect mapNew = new SFormsFastMapDirect();\n\n    for (DirectNode pred : node.predecessors) {\n      SFormsFastMapDirect mapOut = getFilteredOutMap(node.id, pred.id, dgraph, node.id);\n      if (mapNew.isEmpty()) {\n        mapNew = mapOut.getCopy();\n      }\n      else {\n        mergeMaps(mapNew, mapOut);\n      }\n    }\n\n    if (extraVarVersions.containsKey(node.id)) {\n      SFormsFastMapDirect mapExtra = extraVarVersions.get(node.id);\n      if (mapNew.isEmpty()) {\n        mapNew = mapExtra.getCopy();\n      }\n      else {\n        mergeMaps(mapNew, mapExtra);\n      }\n    }\n\n    inVarVersions.put(node.id, mapNew);\n  }\n\n  private SFormsFastMapDirect getFilteredOutMap(String nodeid, String predid, DirectGraph dgraph, String destid) {\n\n    SFormsFastMapDirect mapNew = new SFormsFastMapDirect();\n\n    boolean isFinallyExit = dgraph.mapShortRangeFinallyPaths.containsKey(predid);\n\n    if (nodeid.equals(dgraph.mapNegIfBranch.get(predid))) {\n      if (outNegVarVersions.containsKey(predid)) {\n        mapNew = outNegVarVersions.get(predid).getCopy();\n      }\n    }\n    else if (outVarVersions.containsKey(predid)) {\n      mapNew = outVarVersions.get(predid).getCopy();\n    }\n\n    if (isFinallyExit) {\n\n      SFormsFastMapDirect mapNewTemp = mapNew.getCopy();\n\n      SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect();\n\n      String exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.get(predid);\n      boolean isExceptionMonitorExit = (exceptionDest != null && !nodeid.equals(exceptionDest));\n\n      HashSet<String> setLongPathWrapper = new HashSet<>();\n      for (List<FinallyPathWrapper> lstwrapper : dgraph.mapLongRangeFinallyPaths.values()) {\n        for (FinallyPathWrapper finwraplong : lstwrapper) {\n          setLongPathWrapper.add(finwraplong.destination + \"##\" + finwraplong.source);\n        }\n      }\n\n      for (FinallyPathWrapper finwrap : dgraph.mapShortRangeFinallyPaths.get(predid)) {\n        SFormsFastMapDirect map;\n\n        boolean recFinally = dgraph.mapShortRangeFinallyPaths.containsKey(finwrap.source);\n\n        if (recFinally) {\n          // recursion\n          map = getFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid);\n        }\n        else {\n          if (finwrap.entry.equals(dgraph.mapNegIfBranch.get(finwrap.source))) {\n            map = outNegVarVersions.get(finwrap.source);\n          }\n          else {\n            map = outVarVersions.get(finwrap.source);\n          }\n        }\n\n        // false path?\n        boolean isFalsePath;\n\n        if (recFinally) {\n          isFalsePath = !finwrap.destination.equals(nodeid);\n        }\n        else {\n          isFalsePath = !setLongPathWrapper.contains(destid + \"##\" + finwrap.source);\n        }\n\n        if (isFalsePath) {\n          mapNewTemp.complement(map);\n        }\n        else {\n          if (mapTrueSource.isEmpty()) {\n            if (map != null) {\n              mapTrueSource = map.getCopy();\n            }\n          }\n          else {\n            mergeMaps(mapTrueSource, map);\n          }\n        }\n      }\n\n      if (isExceptionMonitorExit) {\n\n        mapNew = mapTrueSource;\n      }\n      else {\n\n        mapNewTemp.union(mapTrueSource);\n        mapNew.intersection(mapNewTemp);\n\n        if (!mapTrueSource.isEmpty() && !mapNew.isEmpty()) { // FIXME: what for??\n\n          // replace phi versions with corresponding phantom ones\n          HashMap<VarVersionPair, VarVersionPair> mapPhantom = phantomexitnodes.get(predid);\n          if (mapPhantom == null) {\n            mapPhantom = new HashMap<>();\n          }\n\n          SFormsFastMapDirect mapExitVar = mapNew.getCopy();\n          mapExitVar.complement(mapTrueSource);\n\n          for (Entry<Integer, FastSparseSet<Integer>> ent : mapExitVar.entryList()) {\n            for (Integer version : ent.getValue()) {\n\n              Integer varindex = ent.getKey();\n              VarVersionPair exitvar = new VarVersionPair(varindex, version);\n              FastSparseSet<Integer> newSet = mapNew.get(varindex);\n\n              // remove the actual exit version\n              newSet.remove(version);\n\n              // get or create phantom version\n              VarVersionPair phantomvar = mapPhantom.get(exitvar);\n              if (phantomvar == null) {\n                Integer newversion = getNextFreeVersion(exitvar.var, null);\n                phantomvar = new VarVersionPair(exitvar.var, newversion.intValue());\n\n                VarVersionNode exitnode = ssuversions.nodes.getWithKey(exitvar);\n                VarVersionNode phantomnode = ssuversions.createNode(phantomvar);\n                phantomnode.flags |= VarVersionNode.FLAG_PHANTOM_FIN_EXIT;\n\n                VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.EDGE_PHANTOM, exitnode, phantomnode);\n                exitnode.addSuccessor(edge);\n                phantomnode.addPredecessor(edge);\n\n                mapPhantom.put(exitvar, phantomvar);\n              }\n\n              // add phantom version\n              newSet.add(phantomvar.version);\n            }\n          }\n\n          if (!mapPhantom.isEmpty()) {\n            phantomexitnodes.put(predid, mapPhantom);\n          }\n        }\n      }\n    }\n\n    return mapNew;\n  }\n\n  private static SFormsFastMapDirect mergeMaps(SFormsFastMapDirect mapTo, SFormsFastMapDirect map2) {\n\n    if (map2 != null && !map2.isEmpty()) {\n      mapTo.union(map2);\n    }\n\n    return mapTo;\n  }\n\n  private static boolean mapsEqual(SFormsFastMapDirect map1, SFormsFastMapDirect map2) {\n\n    if (map1 == null) {\n      return map2 == null;\n    }\n    else if (map2 == null) {\n      return false;\n    }\n\n    if (map1.size() != map2.size()) {\n      return false;\n    }\n\n    for (Entry<Integer, FastSparseSet<Integer>> ent2 : map2.entryList()) {\n      if (!Objects.equals(map1.get(ent2.getKey()), ent2.getValue())) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n\n  private void setCurrentVar(SFormsFastMapDirect varmap, Integer var, Integer vers) {\n    FastSparseSet<Integer> set = factory.spawnEmptySet();\n    set.add(vers);\n    varmap.put(var, set);\n  }\n\n  private void setCatchMaps(Statement stat, DirectGraph dgraph, FlattenStatementsHelper flatthelper) {\n\n    SFormsFastMapDirect map;\n\n    switch (stat.type) {\n      case CATCH_ALL, TRY_CATCH -> {\n        List<VarExprent> lstVars;\n        if (stat.type == StatementType.CATCH_ALL) {\n          lstVars = ((CatchAllStatement)stat).getVars();\n        }\n        else {\n          lstVars = ((CatchStatement)stat).getVars();\n        }\n\n        for (int i = 1; i < stat.getStats().size(); i++) {\n          int varindex = lstVars.get(i - 1).getIndex();\n          int version = getNextFreeVersion(varindex, stat); // == 1\n\n          map = new SFormsFastMapDirect();\n          setCurrentVar(map, varindex, version);\n\n          extraVarVersions.put(dgraph.nodes.getWithKey(flatthelper.getMapDestinationNodes().get(stat.getStats().get(i).id)[0]).id, map);\n          //ssuversions.createOrGetNode(new VarVersionPair(varindex, version));\n          ssuversions.createNode(new VarVersionPair(varindex, version));\n        }\n      }\n    }\n\n    for (Statement st : stat.getStats()) {\n      setCatchMaps(st, dgraph, flatthelper);\n    }\n  }\n\n  private SFormsFastMapDirect createFirstMap(StructMethod mt, RootStatement root) {\n    boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);\n\n    MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n\n    int paramcount = md.params.length + (thisvar ? 1 : 0);\n\n    int varindex = 0;\n    SFormsFastMapDirect map = new SFormsFastMapDirect();\n    for (int i = 0; i < paramcount; i++) {\n      int version = getNextFreeVersion(varindex, root); // == 1\n\n      FastSparseSet<Integer> set = factory.spawnEmptySet();\n      set.add(version);\n      map.put(varindex, set);\n      ssuversions.createNode(new VarVersionPair(varindex, version));\n\n      if (thisvar) {\n        if (i == 0) {\n          varindex++;\n        }\n        else {\n          varindex += md.params[i - 1].getStackSize();\n        }\n      }\n      else {\n        varindex += md.params[i].getStackSize();\n      }\n    }\n\n    return map;\n  }\n\n  private static Integer getFirstProtectedRange(Statement stat) {\n\n    while (true) {\n      Statement parent = stat.getParent();\n\n      if (parent == null) {\n        break;\n      }\n\n      if (parent.type == StatementType.CATCH_ALL ||\n          parent.type == StatementType.TRY_CATCH) {\n        if (parent.getFirst() == stat) {\n          return parent.id;\n        }\n      }\n      else if (parent.type == StatementType.SYNCHRONIZED) {\n        if (((SynchronizedStatement)parent).getBody() == stat) {\n          return parent.id;\n        }\n      }\n\n      stat = parent;\n    }\n\n    return null;\n  }\n\n  public VarVersionsGraph getSsuversions() {\n    return ssuversions;\n  }\n\n  public SFormsFastMapDirect getLiveVarVersionsMap(VarVersionPair varpaar) {\n\n\n    VarVersionNode node = ssuversions.nodes.getWithKey(varpaar);\n    if (node != null) {\n      return node.live;\n    }\n\n    return null;\n  }\n\n  public HashMap<VarVersionPair, Integer> getMapVersionFirstRange() {\n    return mapVersionFirstRange;\n  }\n\n  public HashMap<Integer, Integer> getMapFieldVars() {\n    return mapFieldVars;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.Instruction;\nimport org.jetbrains.java.decompiler.code.SimpleInstructionSequence;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\npublic class BasicBlockStatement extends Statement {\n  private final BasicBlock block;\n\n  public BasicBlockStatement(BasicBlock block) {\n    super(StatementType.BASIC_BLOCK, block.id);\n    this.block = block;\n\n    CounterContainer container = DecompilerContext.getCounterContainer();\n    if (id >= container.getCounter(CounterContainer.STATEMENT_COUNTER)) {\n      container.setCounter(CounterContainer.STATEMENT_COUNTER, id + 1);\n    }\n\n    Instruction instr = block.getLastInstruction();\n    if (instr != null) {\n      if (instr.group == CodeConstants.GROUP_JUMP && instr.opcode != CodeConstants.opc_goto) {\n        lastBasicType = StatementType.IF;\n      }\n      else if (instr.group == CodeConstants.GROUP_SWITCH) {\n        lastBasicType = StatementType.SWITCH;\n      }\n    }\n\n    buildMonitorFlags();\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer tb = ExprProcessor.listToJava(varDefinitions, indent, tracer);\n    tb.append(ExprProcessor.listToJava(exprents, indent, tracer));\n    return tb;\n  }\n\n  @Override\n  public Statement getSimpleCopy() {\n    int id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER);\n\n    SimpleInstructionSequence seq = new SimpleInstructionSequence();\n    for (int i = 0; i < block.getSeq().length(); i++) {\n      seq.addInstruction(block.getSeq().getInstr(i).clone(), -1);\n    }\n\n    return new BasicBlockStatement(new BasicBlock(id, seq));\n  }\n\n  public BasicBlock getBlock() {\n    return block;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.DecHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\npublic final class CatchAllStatement extends Statement {\n\n  private Statement handler;\n\n  private boolean isFinally;\n\n  private VarExprent monitor;\n\n  private final List<VarExprent> vars = new ArrayList<>();\n\n  // *****************************************************************************\n  // constructors\n  // *****************************************************************************\n\n  private CatchAllStatement() {\n    super(StatementType.CATCH_ALL);\n  }\n\n  private CatchAllStatement(Statement head, Statement handler) {\n\n    this();\n\n    first = head;\n    stats.addWithKey(head, head.id);\n\n    this.handler = handler;\n    stats.addWithKey(handler, handler.id);\n\n    List<StatEdge> lstSuccs = head.getSuccessorEdges(EdgeType.DIRECT_ALL);\n    if (!lstSuccs.isEmpty()) {\n      StatEdge edge = lstSuccs.get(0);\n      if (edge.getType() == EdgeType.REGULAR) {\n        post = edge.getDestination();\n      }\n    }\n\n    vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),\n                            new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Throwable\"),\n                            DecompilerContext.getVarProcessor()));\n  }\n\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  public static Statement isHead(Statement head) {\n    if (head.getLastBasicType() != StatementType.GENERAL) {\n      return null;\n    }\n\n    Set<Statement> setHandlers = DecHelper.getUniquePredExceptions(head);\n    if (setHandlers.size() != 1) {\n      return null;\n    }\n\n    for (StatEdge edge : head.getSuccessorEdges(EdgeType.EXCEPTION)) {\n      Statement exc = edge.getDestination();\n\n      if (edge.getExceptions() == null && exc.getLastBasicType() == StatementType.GENERAL && setHandlers.contains(exc)) {\n        List<StatEdge> lstSuccs = exc.getSuccessorEdges(EdgeType.DIRECT_ALL);\n        if (lstSuccs.isEmpty() || lstSuccs.get(0).getType() != EdgeType.REGULAR) {\n\n          if (head.isMonitorEnter() || exc.isMonitorEnter()) {\n            return null;\n          }\n\n          if (DecHelper.checkStatementExceptions(Arrays.asList(head, exc))) {\n            return new CatchAllStatement(head, exc);\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    String new_line_separator = DecompilerContext.getNewLineSeparator();\n\n    TextBuffer buf = new TextBuffer();\n\n    buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer));\n\n    boolean labeled = isLabeled();\n    if (labeled) {\n      buf.appendIndent(indent).append(\"label\").append(Integer.toString(id)).append(\":\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    List<StatEdge> lstSuccs = first.getSuccessorEdges(EdgeType.DIRECT_ALL);\n    if (first.type == StatementType.TRY_CATCH && first.varDefinitions.isEmpty() && isFinally &&\n        !labeled && !first.isLabeled() && (lstSuccs.isEmpty() || !lstSuccs.get(0).explicit)) {\n      TextBuffer content = ExprProcessor.jmpWrapper(first, indent, true, tracer);\n      content.setLength(content.length() - new_line_separator.length());\n      tracer.incrementCurrentSourceLine(-1);\n      buf.append(content);\n    }\n    else {\n      buf.appendIndent(indent).append(\"try {\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n      buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer));\n      buf.appendIndent(indent).append(\"}\");\n    }\n\n    buf.append(isFinally ? \" finally\" :\n               \" catch (\" + vars.get(0).toJava(indent, tracer) + \")\").append(\" {\").appendLineSeparator();\n    tracer.incrementCurrentSourceLine();\n\n    if (monitor != null) {\n      buf.appendIndent(indent+1).append(\"if (\").append(monitor.toJava(indent, tracer)).append(\") {\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    buf.append(ExprProcessor.jmpWrapper(handler, indent + 1 + (monitor != null ? 1 : 0), true, tracer));\n\n    if (monitor != null) {\n      buf.appendIndent(indent + 1).append(\"}\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    buf.appendIndent(indent).append(\"}\").appendLineSeparator();\n    tracer.incrementCurrentSourceLine();\n\n    return buf;\n  }\n\n  @Override\n  public void replaceStatement(Statement oldstat, Statement newstat) {\n\n    if (handler == oldstat) {\n      handler = newstat;\n    }\n\n    super.replaceStatement(oldstat, newstat);\n  }\n\n  @Override\n  public Statement getSimpleCopy() {\n\n    CatchAllStatement cas = new CatchAllStatement();\n\n    cas.isFinally = this.isFinally;\n\n    if (this.monitor != null) {\n      cas.monitor = new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),\n                                   VarType.VARTYPE_INT,\n                                   DecompilerContext.getVarProcessor());\n    }\n\n    if (!this.vars.isEmpty()) {\n      cas.vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),\n                              new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Throwable\"),\n                              DecompilerContext.getVarProcessor()));\n    }\n\n    return cas;\n  }\n\n  @Override\n  public void initSimpleCopy() {\n    first = stats.get(0);\n    handler = stats.get(1);\n  }\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public Statement getHandler() {\n    return handler;\n  }\n\n  public boolean isFinally() {\n    return isFinally;\n  }\n\n  public void setFinally(boolean isFinally) {\n    this.isFinally = isFinally;\n  }\n\n  public VarExprent getMonitor() {\n    return monitor;\n  }\n\n  public void setMonitor(VarExprent monitor) {\n    this.monitor = monitor;\n  }\n\n  public List<VarExprent> getVars() {\n    return vars;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.DecHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Set;\n\npublic final class CatchStatement extends Statement {\n  private final List<List<String>> exctstrings = new ArrayList<>();\n  private final List<VarExprent> vars = new ArrayList<>();\n\n  // *****************************************************************************\n  // constructors\n  // *****************************************************************************\n\n  private CatchStatement() {\n    super(StatementType.TRY_CATCH);\n  }\n\n  private CatchStatement(Statement head, Statement next, Set<Statement> setHandlers) {\n    this();\n\n    first = head;\n    stats.addWithKey(first, first.id);\n\n    for (StatEdge edge : head.getSuccessorEdges(EdgeType.EXCEPTION)) {\n      Statement stat = edge.getDestination();\n\n      if (setHandlers.contains(stat)) {\n        stats.addWithKey(stat, stat.id);\n        exctstrings.add(new ArrayList<>(edge.getExceptions()));\n\n        vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),\n                                new VarType(CodeConstants.TYPE_OBJECT, 0, edge.getExceptions().get(0)),\n                                // FIXME: for now simply the first type. Should get the first common superclass when possible.\n                                DecompilerContext.getVarProcessor()));\n      }\n    }\n\n    if (next != null) {\n      post = next;\n    }\n  }\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  public static Statement isHead(Statement head) {\n    if (head.getLastBasicType() != StatementType.GENERAL) {\n      return null;\n    }\n\n    Set<Statement> setHandlers = DecHelper.getUniquePredExceptions(head);\n    if (!setHandlers.isEmpty()) {\n      int hnextcount = 0; // either no statements with connection to next, or more than 1\n\n      Statement next = null;\n      List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(EdgeType.DIRECT_ALL);\n      if (!lstHeadSuccs.isEmpty() && lstHeadSuccs.get(0).getType() == EdgeType.REGULAR) {\n        next = lstHeadSuccs.get(0).getDestination();\n        hnextcount = 2;\n      }\n\n      for (StatEdge edge : head.getSuccessorEdges(EdgeType.EXCEPTION)) {\n        Statement stat = edge.getDestination();\n\n        boolean handlerok = true;\n\n        if (edge.getExceptions() != null && setHandlers.contains(stat)) {\n          if (stat.getLastBasicType() != StatementType.GENERAL) {\n            handlerok = false;\n          }\n          else {\n            List<StatEdge> lstStatSuccs = stat.getSuccessorEdges(EdgeType.DIRECT_ALL);\n            if (!lstStatSuccs.isEmpty() && lstStatSuccs.get(0).getType() == EdgeType.REGULAR) {\n\n              Statement statn = lstStatSuccs.get(0).getDestination();\n\n              if (next == null) {\n                next = statn;\n              }\n              else if (next != statn) {\n                handlerok = false;\n              }\n\n              if (handlerok) {\n                hnextcount++;\n              }\n            }\n          }\n        }\n        else {\n          handlerok = false;\n        }\n\n        if (!handlerok) {\n          setHandlers.remove(stat);\n        }\n      }\n\n      if (hnextcount != 1 && !setHandlers.isEmpty()) {\n        List<Statement> lst = new ArrayList<>();\n        lst.add(head);\n        lst.addAll(setHandlers);\n\n        for (Statement st : lst) {\n          if (st.isMonitorEnter()) {\n            return null;\n          }\n        }\n\n        if (DecHelper.checkStatementExceptions(lst)) {\n          return new CatchStatement(head, next, setHandlers);\n        }\n      }\n    }\n    return null;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n\n    buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer));\n\n    if (isLabeled()) {\n      buf.appendIndent(indent).append(\"label\").append(Integer.toString(id)).append(\":\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    buf.appendIndent(indent).append(\"try {\").appendLineSeparator();\n    tracer.incrementCurrentSourceLine();\n\n    buf.append(ExprProcessor.jmpWrapper(first, indent + 1, true, tracer));\n    buf.appendIndent(indent).append(\"}\");\n\n    for (int i = 1; i < stats.size(); i++) {\n      Statement stat = stats.get(i);\n      // map first instruction storing the exception to the catch statement\n      BasicBlock block = stat.getBasichead().getBlock();\n      if (!block.getSeq().isEmpty() && block.getInstruction(0).opcode == CodeConstants.opc_astore) {\n        Integer offset = block.getOriginalOffset(0);\n        if (offset > -1) tracer.addMapping(offset);\n      }\n\n      buf.append(\" catch (\");\n\n      List<String> exception_types = exctstrings.get(i - 1);\n      if (exception_types.size() > 1) { // multi-catch, Java 7 style\n        for (int exc_index = 1; exc_index < exception_types.size(); ++exc_index) {\n          VarType exc_type = new VarType(CodeConstants.TYPE_OBJECT, 0, exception_types.get(exc_index));\n          String exc_type_name = ExprProcessor.getCastTypeName(exc_type, Collections.emptyList());\n\n          buf.append(exc_type_name).append(\" | \");\n        }\n      }\n      buf.append(vars.get(i - 1).toJava(indent, tracer));\n      buf.append(\") {\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n      buf.append(ExprProcessor.jmpWrapper(stat, indent + 1, false, tracer)).appendIndent(indent)\n        .append(\"}\");\n    }\n    buf.appendLineSeparator();\n\n    tracer.incrementCurrentSourceLine();\n    return buf;\n  }\n\n  @Override\n  public Statement getSimpleCopy() {\n    CatchStatement cs = new CatchStatement();\n\n    for (List<String> exc : this.exctstrings) {\n      cs.exctstrings.add(new ArrayList<>(exc));\n      cs.vars.add(new VarExprent(DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.VAR_COUNTER),\n                                 new VarType(CodeConstants.TYPE_OBJECT, 0, exc.get(0)),\n                                 DecompilerContext.getVarProcessor()));\n    }\n\n    return cs;\n  }\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public List<VarExprent> getVars() {\n    return vars;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\npublic final class DoStatement extends Statement {\n  private final List<@Nullable Exprent> initExprent = new ArrayList<>();\n  private final List<@Nullable Exprent> conditionExprent = new ArrayList<>();\n  private final List<@Nullable Exprent> incExprent = new ArrayList<>();\n\n  private @NotNull LoopType loopType;\n\n  private DoStatement() {\n    super(StatementType.DO);\n    initExprent.add(null);\n    conditionExprent.add(null);\n    incExprent.add(null);\n    loopType = LoopType.DO;\n  }\n\n  private DoStatement(Statement head) {\n    this();\n    first = head;\n    stats.addWithKey(first, first.id);\n    // post is always null!\n  }\n\n  public static @Nullable Statement isHead(Statement head) {\n    if (head.getLastBasicType() == StatementType.GENERAL && !head.isMonitorEnter()) {\n      // at most one outgoing edge\n      StatEdge edge = null;\n      List<StatEdge> successorEdges = head.getSuccessorEdges(EdgeType.DIRECT_ALL);\n      if (!successorEdges.isEmpty()) {\n        edge = successorEdges.get(0);\n      }\n      // regular loop\n      if (edge != null && edge.getType() == EdgeType.REGULAR && edge.getDestination() == head) {\n        return new DoStatement(head);\n      }\n      // continues\n      if (head.type != StatementType.DO && (edge == null || edge.getType() != EdgeType.REGULAR) &&\n          head.getContinueSet().contains(head.getBasichead())) {\n        return new DoStatement(head);\n      }\n    }\n    return null;\n  }\n\n  @Override\n  public @NotNull TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n    buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer));\n    if (isLabeled()) {\n      buf.appendIndent(indent).append(\"label\").append(Integer.toString(id)).append(\":\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n    switch (loopType) {\n      case DO -> {\n        buf.appendIndent(indent).append(\"while(true) {\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n        buf.append(ExprProcessor.jmpWrapper(first, indent + 1, false, tracer));\n        buf.appendIndent(indent).append(\"}\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n      case DO_WHILE -> {\n        buf.appendIndent(indent).append(\"do {\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n        buf.append(ExprProcessor.jmpWrapper(first, indent + 1, false, tracer));\n        buf.appendIndent(indent).append(\"} while(\").append(\n          Objects.requireNonNull(conditionExprent.get(0)).toJava(indent, tracer)).append(\");\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n      case WHILE -> {\n        buf.appendIndent(indent).append(\"while(\").append(\n          Objects.requireNonNull(conditionExprent.get(0)).toJava(indent, tracer)).append(\") {\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n        buf.append(ExprProcessor.jmpWrapper(first, indent + 1, false, tracer));\n        buf.appendIndent(indent).append(\"}\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n      case FOR -> {\n        buf.appendIndent(indent).append(\"for(\");\n        Exprent firstInitExprent = initExprent.get(0);\n        if (firstInitExprent != null) {\n          buf.append(firstInitExprent.toJava(indent, tracer));\n        }\n        Exprent firstIncExprent = Objects.requireNonNull(incExprent.get(0));\n        buf.append(\"; \")\n          .append(Objects.requireNonNull(conditionExprent.get(0)).toJava(indent, tracer)).append(\"; \")\n          .append(firstIncExprent.toJava(indent, tracer)).append(\") {\")\n          .appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n        buf.append(ExprProcessor.jmpWrapper(first, indent + 1, false, tracer));\n        buf.appendIndent(indent).append(\"}\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n    }\n    return buf;\n  }\n\n  @Override\n  public @NotNull List<Object> getSequentialObjects() {\n    List<Object> lst = new ArrayList<>();\n    switch (loopType) {\n      case FOR:\n        if (getInitExprent() != null) {\n          lst.add(getInitExprent());\n        }\n      case WHILE:\n        lst.add(getConditionExprent());\n    }\n    lst.add(first);\n    switch (loopType) {\n      case DO_WHILE -> lst.add(getConditionExprent());\n      case FOR -> lst.add(getIncExprent());\n    }\n    return lst;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExpr, Exprent newExpr) {\n    if (initExprent.get(0) == oldExpr) {\n      initExprent.set(0, newExpr);\n    }\n    if (conditionExprent.get(0) == oldExpr) {\n      conditionExprent.set(0, newExpr);\n    }\n    if (incExprent.get(0) == oldExpr) {\n      incExprent.set(0, newExpr);\n    }\n  }\n\n  @Override\n  public @NotNull Statement getSimpleCopy() {\n    return new DoStatement();\n  }\n\n  public @NotNull List<Exprent> getInitExprentList() {\n    return initExprent;\n  }\n\n  public @NotNull List<Exprent> getConditionExprentList() {\n    return conditionExprent;\n  }\n\n  public @NotNull List<Exprent> getIncExprentList() {\n    return incExprent;\n  }\n\n  public @Nullable Exprent getConditionExprent() {\n    return conditionExprent.get(0);\n  }\n\n  public void setConditionExprent(Exprent conditionExprent) {\n    this.conditionExprent.set(0, conditionExprent);\n  }\n\n  public @Nullable Exprent getIncExprent() {\n    return incExprent.get(0);\n  }\n\n  public void setIncExprent(Exprent incExprent) {\n    this.incExprent.set(0, incExprent);\n  }\n\n  public @Nullable Exprent getInitExprent() {\n    return initExprent.get(0);\n  }\n\n  public void setInitExprent(Exprent initExprent) {\n    this.initExprent.set(0, initExprent);\n  }\n\n  public @NotNull LoopType getLoopType() {\n    return loopType;\n  }\n\n  public void setLoopType(@NotNull LoopType loopType) {\n    this.loopType = loopType;\n  }\n\n  public enum LoopType {\n    DO,\n    DO_WHILE,\n    WHILE,\n    FOR\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/DummyExitStatement.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class DummyExitStatement extends Statement {\n  public Set<Integer> bytecode = null;  // offsets of bytecode instructions mapped to dummy exit\n\n  public DummyExitStatement() {\n    super(StatementType.DUMMY_EXIT);\n  }\n\n  public void addBytecodeOffsets(Collection<Integer> bytecodeOffsets) {\n    if (bytecodeOffsets != null && !bytecodeOffsets.isEmpty()) {\n      if (bytecode == null) {\n        bytecode = new HashSet<>(bytecodeOffsets);\n      }\n      else {\n        bytecode.addAll(bytecodeOffsets);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.Collection;\nimport java.util.HashSet;\n\n\npublic class GeneralStatement extends Statement {\n\n  // *****************************************************************************\n  // constructors\n  // *****************************************************************************\n\n  private GeneralStatement() {\n    super(StatementType.GENERAL);\n  }\n\n  public GeneralStatement(Statement head, Collection<? extends Statement> statements, Statement post) {\n\n    this();\n\n    first = head;\n    stats.addWithKey(head, head.id);\n\n    HashSet<Statement> set = new HashSet<>(statements);\n    set.remove(head);\n\n    for (Statement st : set) {\n      stats.addWithKey(st, st.id);\n    }\n\n    this.post = post;\n  }\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n\n    if (isLabeled()) {\n      buf.appendIndent(indent).append(\"label\").append(Integer.toString(id)).append(\":\").appendLineSeparator();\n    }\n\n    buf.appendIndent(indent).append(\"abstract statement {\").appendLineSeparator();\n    for (Statement stat : stats) {\n      buf.append(stat.toJava(indent + 1, tracer));\n    }\n    buf.appendIndent(indent).append(\"}\");\n\n    return buf;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.DecHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent;\nimport org.jetbrains.java.decompiler.struct.match.IMatchable;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\nimport org.jetbrains.java.decompiler.util.TextUtil;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\npublic final class IfStatement extends Statement {\n\n  public static final int IFTYPE_IF = 0;\n  public static final int IFTYPE_IFELSE = 1;\n\n  public int iftype;\n\n  // *****************************************************************************\n  // private fields\n  // *****************************************************************************\n\n  private Statement ifstat;\n  private Statement elsestat;\n\n  private StatEdge ifedge;\n  private StatEdge elseedge;\n\n  private boolean negated = false;\n\n  private final List<Exprent> headexprent = new ArrayList<>(1); // contains IfExprent\n\n  // *****************************************************************************\n  // constructors\n  // *****************************************************************************\n\n  private IfStatement() {\n    super(StatementType.IF);\n\n    headexprent.add(null);\n  }\n\n  private IfStatement(Statement head, int regedges, Statement postst) {\n\n    this();\n\n    first = head;\n    stats.addWithKey(head, head.id);\n\n    List<StatEdge> lstHeadSuccs = head.getSuccessorEdges(EdgeType.DIRECT_ALL);\n\n    switch (regedges) {\n      case 0 -> {\n        ifstat = null;\n        elsestat = null;\n      }\n      case 1 -> {\n        ifstat = null;\n        elsestat = null;\n\n        StatEdge edgeif = lstHeadSuccs.get(1);\n        if (edgeif.getType() != EdgeType.REGULAR) {\n          post = lstHeadSuccs.get(0).getDestination();\n        }\n        else {\n          post = edgeif.getDestination();\n          negated = true;\n        }\n      }\n      case 2 -> {\n        elsestat = lstHeadSuccs.get(0).getDestination();\n        ifstat = lstHeadSuccs.get(1).getDestination();\n\n        List<StatEdge> lstSucc = ifstat.getSuccessorEdges(EdgeType.REGULAR);\n        List<StatEdge> lstSucc1 = elsestat.getSuccessorEdges(EdgeType.REGULAR);\n\n        if (ifstat.getPredecessorEdges(EdgeType.REGULAR).size() > 1 || lstSucc.size() > 1) {\n          post = ifstat;\n        }\n        else if (elsestat.getPredecessorEdges(EdgeType.REGULAR).size() > 1 || lstSucc1.size() > 1) {\n          post = elsestat;\n        }\n        else {\n          if (lstSucc.size() == 0) {\n            post = elsestat;\n          }\n          else if (lstSucc1.size() == 0) {\n            post = ifstat;\n          }\n        }\n\n        if (ifstat == post) {\n          if (elsestat != post) {\n            ifstat = elsestat;\n            negated = true;\n          }\n          else {\n            ifstat = null;\n          }\n          elsestat = null;\n        }\n        else if (elsestat == post) {\n          elsestat = null;\n        }\n        else {\n          post = postst;\n        }\n\n        if (elsestat == null) {\n          regedges = 1;  // if without else\n        }\n      }\n    }\n\n    ifedge = lstHeadSuccs.get(negated ? 0 : 1);\n    elseedge = (regedges == 2) ? lstHeadSuccs.get(negated ? 1 : 0) : null;\n\n    iftype = (regedges == 2) ? IFTYPE_IFELSE : IFTYPE_IF;\n\n    if (iftype == IFTYPE_IF) {\n      if (regedges == 0) {\n        StatEdge edge = lstHeadSuccs.get(0);\n        head.removeSuccessor(edge);\n        edge.setSource(this);\n        this.addSuccessor(edge);\n      }\n      else if (regedges == 1) {\n        StatEdge edge = lstHeadSuccs.get(negated ? 1 : 0);\n        head.removeSuccessor(edge);\n      }\n    }\n\n    if (ifstat != null) {\n      stats.addWithKey(ifstat, ifstat.id);\n    }\n\n    if (elsestat != null) {\n      stats.addWithKey(elsestat, elsestat.id);\n    }\n\n    if (post == head) {\n      post = this;\n    }\n  }\n\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  public static Statement isHead(Statement head) {\n\n    if (head.type == StatementType.BASIC_BLOCK && head.getLastBasicType() == StatementType.IF) {\n      int regsize = head.getSuccessorEdges(EdgeType.REGULAR).size();\n\n      Statement p = null;\n\n      boolean ok = (regsize < 2);\n      if (!ok) {\n        List<Statement> lst = new ArrayList<>();\n        if (DecHelper.isChoiceStatement(head, lst)) {\n          p = lst.remove(0);\n\n          for (Statement st : lst) {\n            if (st.isMonitorEnter()) {\n              return null;\n            }\n          }\n\n          ok = DecHelper.checkStatementExceptions(lst);\n        }\n      }\n\n      if (ok) {\n        return new IfStatement(head, regsize, p);\n      }\n    }\n\n    return null;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n\n    buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer));\n    buf.append(first.toJava(indent, tracer));\n\n    if (isLabeled()) {\n      buf.appendIndent(indent).append(\"label\").append(Integer.toString(id)).append(\":\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    buf.appendIndent(indent).append(headexprent.get(0).toJava(indent, tracer)).append(\" {\").appendLineSeparator();\n    tracer.incrementCurrentSourceLine();\n\n    if (ifstat == null) {\n      boolean semicolon = false;\n      if (ifedge.explicit) {\n        semicolon = true;\n        if (ifedge.getType() == EdgeType.BREAK) {\n          // break\n          buf.appendIndent(indent + 1).append(\"break\");\n        }\n        else {\n          // continue\n          buf.appendIndent(indent + 1).append(\"continue\");\n        }\n\n        if (ifedge.labeled) {\n          buf.append(\" label\").append(Integer.toString(ifedge.closure.id));\n        }\n      }\n      if(semicolon) {\n        buf.append(\";\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n    }\n    else {\n      buf.append(ExprProcessor.jmpWrapper(ifstat, indent + 1, true, tracer));\n    }\n\n    boolean elseif = false;\n\n    if (elsestat != null) {\n      if (elsestat.type == StatementType.IF\n          && elsestat.varDefinitions.isEmpty() && elsestat.getFirst().getExprents().isEmpty() &&\n          !elsestat.isLabeled() &&\n          (elsestat.getSuccessorEdges(EdgeType.DIRECT_ALL).isEmpty()\n           || !elsestat.getSuccessorEdges(EdgeType.DIRECT_ALL).get(0).explicit)) { // else if\n        buf.appendIndent(indent).append(\"} else \");\n\n        TextBuffer content = ExprProcessor.jmpWrapper(elsestat, indent, false, tracer);\n        content.setStart(TextUtil.getIndentString(indent).length());\n        buf.append(content);\n\n        elseif = true;\n      }\n      else {\n        BytecodeMappingTracer else_tracer = new BytecodeMappingTracer(tracer.getCurrentSourceLine() + 1);\n        TextBuffer content = ExprProcessor.jmpWrapper(elsestat, indent + 1, false, else_tracer);\n\n        if (content.length() > 0) {\n          buf.appendIndent(indent).append(\"} else {\").appendLineSeparator();\n\n          tracer.setCurrentSourceLine(else_tracer.getCurrentSourceLine());\n          tracer.addTracer(else_tracer);\n\n          buf.append(content);\n        }\n      }\n    }\n\n    if (!elseif) {\n      buf.appendIndent(indent).append(\"}\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    return buf;\n  }\n\n  @Override\n  public void initExprents() {\n\n    IfExprent ifexpr = (IfExprent)first.getExprents().remove(first.getExprents().size() - 1);\n\n    if (negated) {\n      ifexpr = (IfExprent)ifexpr.copy();\n      ifexpr.negateIf();\n    }\n\n    headexprent.set(0, ifexpr);\n  }\n\n  @Override\n  public List<Object> getSequentialObjects() {\n\n    List<Object> lst = new ArrayList<>(stats);\n    lst.add(1, headexprent.get(0));\n\n    return lst;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldexpr, Exprent newexpr) {\n    if (headexprent.get(0) == oldexpr) {\n      headexprent.set(0, newexpr);\n    }\n  }\n\n  @Override\n  public void replaceStatement(Statement oldstat, Statement newstat) {\n\n    super.replaceStatement(oldstat, newstat);\n\n    if (ifstat == oldstat) {\n      ifstat = newstat;\n    }\n\n    if (elsestat == oldstat) {\n      elsestat = newstat;\n    }\n\n    List<StatEdge> lstSuccs = first.getSuccessorEdges(EdgeType.DIRECT_ALL);\n\n    if (iftype == IFTYPE_IF) {\n      ifedge = lstSuccs.get(0);\n      elseedge = null;\n    }\n    else {\n      StatEdge edge0 = lstSuccs.get(0);\n      StatEdge edge1 = lstSuccs.get(1);\n      if (edge0.getDestination() == ifstat) {\n        ifedge = edge0;\n        elseedge = edge1;\n      }\n      else {\n        ifedge = edge1;\n        elseedge = edge0;\n      }\n    }\n  }\n\n  @Override\n  public Statement getSimpleCopy() {\n\n    IfStatement is = new IfStatement();\n    is.iftype = this.iftype;\n    is.negated = this.negated;\n\n    return is;\n  }\n\n  @Override\n  public void initSimpleCopy() {\n\n    first = stats.get(0);\n\n    List<StatEdge> lstSuccs = first.getSuccessorEdges(EdgeType.DIRECT_ALL);\n    ifedge = lstSuccs.get((iftype == IFTYPE_IF || negated) ? 0 : 1);\n    if (stats.size() > 1) {\n      ifstat = stats.get(1);\n    }\n\n    if (iftype == IFTYPE_IFELSE) {\n      elseedge = lstSuccs.get(negated ? 1 : 0);\n      elsestat = stats.get(2);\n    }\n  }\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public Statement getElsestat() {\n    return elsestat;\n  }\n\n  public void setElsestat(Statement elsestat) {\n    this.elsestat = elsestat;\n  }\n\n  public Statement getIfstat() {\n    return ifstat;\n  }\n\n  public void setIfstat(Statement ifstat) {\n    this.ifstat = ifstat;\n  }\n\n  public boolean isNegated() {\n    return negated;\n  }\n\n  public void setNegated(boolean negated) {\n    this.negated = negated;\n  }\n\n  public List<Exprent> getHeadexprentList() {\n    return headexprent;\n  }\n\n  public IfExprent getHeadexprent() {\n    return (IfExprent)headexprent.get(0);\n  }\n\n  public void setElseEdge(StatEdge elseedge) {\n    this.elseedge = elseedge;\n  }\n\n  public void setIfEdge(StatEdge ifedge) {\n    this.ifedge = ifedge;\n  }\n\n  public StatEdge getIfEdge() {\n    return ifedge;\n  }\n\n  public StatEdge getElseEdge() {\n    return elseedge;\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public IMatchable findObject(MatchNode matchNode, int index) {\n    IMatchable object = super.findObject(matchNode, index);\n    if (object != null) {\n      return object;\n    }\n\n    if (matchNode.getType() == MatchNode.MATCHNODE_EXPRENT) {\n      String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);\n      if (\"head\".equals(position)) {\n        return getHeadexprent();\n      }\n    }\n\n    return null;\n  }\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (!super.match(matchNode, engine)) {\n      return false;\n    }\n\n    Integer type = (Integer)matchNode.getRuleValue(MatchProperties.STATEMENT_IFTYPE);\n    return type == null || this.iftype == type;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\npublic class RootStatement extends Statement {\n  private final DummyExitStatement dummyExit;\n\n  public RootStatement(Statement head, DummyExitStatement dummyExit) {\n    super(StatementType.ROOT);\n\n    first = head;\n    this.dummyExit = dummyExit;\n\n    stats.addWithKey(first, first.id);\n    first.setParent(this);\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    return ExprProcessor.listToJava(varDefinitions, indent, tracer).append(first.toJava(indent, tracer));\n  }\n\n  public DummyExitStatement getDummyExit() {\n    return dummyExit;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.DecHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n\npublic class SequenceStatement extends Statement {\n\n\n  // *****************************************************************************\n  // constructors\n  // *****************************************************************************\n\n  private SequenceStatement() {\n    super(StatementType.SEQUENCE);\n  }\n\n  public SequenceStatement(List<? extends Statement> lst) {\n\n    this();\n\n    lastBasicType = lst.get(lst.size() - 1).getLastBasicType();\n\n    for (Statement st : lst) {\n      stats.addWithKey(st, st.id);\n    }\n\n    first = stats.get(0);\n  }\n\n  private SequenceStatement(Statement head, Statement tail) {\n\n    this(Arrays.asList(head, tail));\n\n    List<StatEdge> lstSuccs = tail.getSuccessorEdges(EdgeType.DIRECT_ALL);\n    if (!lstSuccs.isEmpty()) {\n      StatEdge edge = lstSuccs.get(0);\n\n      if (edge.getType() == EdgeType.REGULAR && edge.getDestination() != head) {\n        post = edge.getDestination();\n      }\n    }\n  }\n\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  public static Statement isHead2Block(Statement head) {\n\n    if (head.getLastBasicType() != StatementType.GENERAL) {\n      return null;\n    }\n\n    // at most one outgoing edge\n    StatEdge edge = null;\n    List<StatEdge> lstSuccs = head.getSuccessorEdges(EdgeType.DIRECT_ALL);\n    if (!lstSuccs.isEmpty()) {\n      edge = lstSuccs.get(0);\n    }\n\n    if (edge != null && edge.getType() == EdgeType.REGULAR) {\n      Statement stat = edge.getDestination();\n\n      if (stat != head && stat.getPredecessorEdges(EdgeType.REGULAR).size() == 1\n          && !stat.isMonitorEnter()) {\n\n        if (stat.getLastBasicType() == StatementType.GENERAL) {\n          if (DecHelper.checkStatementExceptions(Arrays.asList(head, stat))) {\n            return new SequenceStatement(head, stat);\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n    boolean isLabeled = isLabeled();\n\n    buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer));\n\n    if (isLabeled) {\n      buf.appendIndent(indent++).append(\"label\").append(Integer.toString(id)).append(\": {\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    boolean notEmpty = false;\n\n    for (int i = 0; i < stats.size(); i++) {\n\n      Statement stat = stats.get(i);\n\n      if (i > 0 && notEmpty) {\n        buf.appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n\n      TextBuffer str = ExprProcessor.jmpWrapper(stat, indent, false, tracer);\n      buf.append(str);\n\n      notEmpty = !str.containsOnlyWhitespaces();\n    }\n\n    if (isLabeled) {\n      buf.appendIndent(indent - 1).append(\"}\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    return buf;\n  }\n\n  @Override\n  public Statement getSimpleCopy() {\n    return new SequenceStatement();\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.InstructionSequence;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;\nimport org.jetbrains.java.decompiler.struct.match.IMatchable;\nimport org.jetbrains.java.decompiler.struct.match.MatchEngine;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic abstract class Statement implements IMatchable {\n  public StatementType type;\n  public int id;\n\n  private final Map<EdgeType, List<StatEdge>> mapSuccEdges = new HashMap<>();\n  private final Map<EdgeType, List<StatEdge>> mapPredEdges = new HashMap<>();\n\n  private final Map<EdgeType, List<Statement>> mapSuccStates = new HashMap<>();\n  private final Map<EdgeType, List<Statement>> mapPredStates = new HashMap<>();\n\n  private final HashSet<StatEdge> labelEdges = new HashSet<>();\n  // copied statement, s. deobfuscating of irreducible CFGs\n  private boolean copied = false;\n  // statement as graph\n  protected final VBStyleCollection<Statement, Integer> stats = new VBStyleCollection<>();\n  protected Statement parent;\n  protected Statement first;\n  protected List<Exprent> exprents;\n  protected final List<Exprent> varDefinitions = new ArrayList<>();\n  // relevant for the first stage of processing only\n  // set to null after initializing of the statement structure\n  protected Statement post;\n  protected StatementType lastBasicType = StatementType.GENERAL;\n  protected boolean isMonitorEnter;\n  protected boolean containsMonitorExit;\n  protected HashSet<Statement> continueSet = new HashSet<>();\n\n  //Statement must live only in one Thread\n  private final CancellationManager cancellationManager = DecompilerContext.getCancellationManager();\n\n  {\n    id = DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER);\n  }\n\n  Statement(@NotNull StatementType type) {\n    this.type = type;\n    cancellationManager.checkCanceled();\n  }\n\n  Statement(@NotNull StatementType type, int id) {\n    this.type = type;\n    this.id = id;\n    cancellationManager.checkCanceled();\n  }\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  public void clearTempInformation() {\n\n    post = null;\n    continueSet = null;\n\n    copied = false;\n    // FIXME: used in FlattenStatementsHelper.flattenStatement()! check and remove\n    //lastBasicType = LASTBASICTYPE_GENERAL;\n    isMonitorEnter = false;\n    containsMonitorExit = false;\n\n    processMap(mapSuccEdges);\n    processMap(mapPredEdges);\n    processMap(mapSuccStates);\n    processMap(mapPredStates);\n  }\n\n  private static <T> void processMap(Map<EdgeType, List<T>> map) {\n    map.remove(EdgeType.EXCEPTION);\n\n    List<T> lst = map.get(EdgeType.DIRECT_ALL);\n    if (lst != null) {\n      map.put(EdgeType.ALL, new ArrayList<>(lst));\n    }\n    else {\n      map.remove(EdgeType.ALL);\n    }\n  }\n\n  public void collapseNodesToStatement(Statement stat) {\n    cancellationManager.checkCanceled();\n    Statement head = stat.getFirst();\n    Statement post = stat.getPost();\n\n    VBStyleCollection<Statement, Integer> setNodes = stat.getStats();\n\n    // post edges\n    if (post != null) {\n      for (StatEdge edge : post.getEdges(EdgeType.DIRECT_ALL, EdgeDirection.BACKWARD)) {\n        if (stat.containsStatementStrict(edge.getSource())) {\n          edge.getSource().changeEdgeType(EdgeDirection.FORWARD, edge, EdgeType.BREAK);\n          stat.addLabeledEdge(edge);\n        }\n      }\n    }\n\n    // regular head edges\n    for (StatEdge prededge : head.getAllPredecessorEdges()) {\n      cancellationManager.checkCanceled();\n\n      if (prededge.getType() != EdgeType.EXCEPTION &&\n          stat.containsStatementStrict(prededge.getSource())) {\n        prededge.getSource().changeEdgeType(EdgeDirection.FORWARD, prededge, EdgeType.CONTINUE);\n        stat.addLabeledEdge(prededge);\n      }\n\n      head.removePredecessor(prededge);\n      prededge.getSource().changeEdgeNode(EdgeDirection.FORWARD, prededge, stat);\n      stat.addPredecessor(prededge);\n    }\n\n    if (setNodes.containsKey(first.id)) {\n      first = stat;\n    }\n\n    // exception edges\n    Set<Statement> setHandlers = new HashSet<>(head.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.FORWARD));\n    for (Statement node : setNodes) {\n      setHandlers.retainAll(node.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.FORWARD));\n    }\n\n    if (!setHandlers.isEmpty()) {\n\n      for (StatEdge edge : head.getEdges(EdgeType.EXCEPTION, EdgeDirection.FORWARD)) {\n        Statement handler = edge.getDestination();\n\n        if (setHandlers.contains(handler)) {\n          if (!setNodes.containsKey(handler.id)) {\n            stat.addSuccessor(new StatEdge(stat, handler, edge.getExceptions()));\n          }\n        }\n      }\n\n      for (Statement node : setNodes) {\n        for (StatEdge edge : node.getEdges(EdgeType.EXCEPTION, EdgeDirection.FORWARD)) {\n          if (setHandlers.contains(edge.getDestination())) {\n            node.removeSuccessor(edge);\n          }\n        }\n      }\n    }\n\n    if (post != null &&\n        !stat.getNeighbours(EdgeType.EXCEPTION, EdgeDirection.FORWARD).contains(post)) { // TODO: second condition redundant?\n      stat.addSuccessor(new StatEdge(EdgeType.REGULAR, stat, post));\n    }\n\n\n    // adjust statement collection\n    for (Statement st : setNodes) {\n      stats.removeWithKey(st.id);\n    }\n\n    stats.addWithKey(stat, stat.id);\n\n    stat.setAllParent();\n    stat.setParent(this);\n\n    stat.buildContinueSet();\n    // monitorenter and monitorexit\n    stat.buildMonitorFlags();\n\n    if (stat.type == StatementType.SWITCH) {\n      // special case switch, sorting leaf nodes\n      ((SwitchStatement)stat).sortEdgesAndNodes();\n    }\n  }\n\n  public void setAllParent() {\n    for (Statement st : stats) {\n      st.setParent(this);\n    }\n  }\n\n  public void addLabeledEdge(StatEdge edge) {\n\n    if (edge.closure != null) {\n      edge.closure.getLabelEdges().remove(edge);\n    }\n    edge.closure = this;\n    this.getLabelEdges().add(edge);\n  }\n\n  private void addEdgeDirectInternal(EdgeDirection direction, StatEdge edge, EdgeType edgetype) {\n    Map<EdgeType, List<StatEdge>> mapEdges = direction == EdgeDirection.BACKWARD ? mapPredEdges : mapSuccEdges;\n    Map<EdgeType, List<Statement>> mapStates = direction == EdgeDirection.BACKWARD ? mapPredStates : mapSuccStates;\n\n    mapEdges.computeIfAbsent(edgetype, k -> new ArrayList<>()).add(edge);\n\n    mapStates.computeIfAbsent(edgetype, k -> new ArrayList<>()).add(direction == EdgeDirection.BACKWARD ? edge.getSource() : edge.getDestination());\n  }\n\n  private void addEdgeInternal(EdgeDirection direction, StatEdge edge) {\n    EdgeType type = edge.getType();\n\n    EdgeType[] arrtypes;\n    if (type == EdgeType.EXCEPTION) {\n      arrtypes = new EdgeType[]{EdgeType.ALL, EdgeType.EXCEPTION};\n    }\n    else {\n      arrtypes = new EdgeType[]{EdgeType.ALL, EdgeType.DIRECT_ALL, type};\n    }\n\n    for (EdgeType edgetype : arrtypes) {\n      addEdgeDirectInternal(direction, edge, edgetype);\n    }\n  }\n\n  private void removeEdgeDirectInternal(EdgeDirection direction, StatEdge edge, EdgeType edgetype) {\n\n    Map<EdgeType, List<StatEdge>> mapEdges = direction == EdgeDirection.BACKWARD ? mapPredEdges : mapSuccEdges;\n    Map<EdgeType, List<Statement>> mapStates = direction == EdgeDirection.BACKWARD ? mapPredStates : mapSuccStates;\n\n    List<StatEdge> lst = mapEdges.get(edgetype);\n    if (lst != null) {\n      int index = lst.indexOf(edge);\n      if (index >= 0) {\n        lst.remove(index);\n        mapStates.get(edgetype).remove(index);\n      }\n    }\n  }\n\n  private void removeEdgeInternal(EdgeDirection direction, StatEdge edge) {\n\n    EdgeType type = edge.getType();\n\n    EdgeType[] arrtypes;\n    if (type == EdgeType.EXCEPTION) {\n      arrtypes = new EdgeType[]{EdgeType.ALL, EdgeType.EXCEPTION};\n    }\n    else {\n      arrtypes = new EdgeType[]{EdgeType.ALL, EdgeType.DIRECT_ALL, type};\n    }\n\n    for (EdgeType edgetype : arrtypes) {\n      removeEdgeDirectInternal(direction, edge, edgetype);\n    }\n  }\n\n  public void addPredecessor(StatEdge edge) {\n    addEdgeInternal(EdgeDirection.BACKWARD, edge);\n  }\n\n  public void removePredecessor(StatEdge edge) {\n\n    if (edge == null) {  // FIXME: redundant?\n      return;\n    }\n\n    removeEdgeInternal(EdgeDirection.BACKWARD, edge);\n  }\n\n  public void addSuccessor(StatEdge edge) {\n    addEdgeInternal(EdgeDirection.FORWARD, edge);\n\n    if (edge.closure != null) {\n      edge.closure.getLabelEdges().add(edge);\n    }\n\n    edge.getDestination().addPredecessor(edge);\n  }\n\n  public void removeSuccessor(StatEdge edge) {\n\n    if (edge == null) {\n      return;\n    }\n\n    removeEdgeInternal(EdgeDirection.FORWARD, edge);\n\n    if (edge.closure != null) {\n      edge.closure.getLabelEdges().remove(edge);\n    }\n\n    if (edge.getDestination() != null) {  // TODO: redundant?\n      edge.getDestination().removePredecessor(edge);\n    }\n  }\n\n  // TODO: make obsolete and remove\n  public void removeAllSuccessors(Statement stat) {\n\n    if (stat == null) {\n      return;\n    }\n\n    for (StatEdge edge : getAllSuccessorEdges()) {\n      if (edge.getDestination() == stat) {\n        removeSuccessor(edge);\n      }\n    }\n  }\n\n  public HashSet<Statement> buildContinueSet() {\n    cancellationManager.checkCanceled();\n    continueSet.clear();\n\n    for (Statement st : stats) {\n      continueSet.addAll(st.buildContinueSet());\n      if (st != first) {\n        continueSet.remove(st.getBasichead());\n      }\n    }\n\n    for (StatEdge edge : getEdges(EdgeType.CONTINUE, EdgeDirection.FORWARD)) {\n      continueSet.add(edge.getDestination().getBasichead());\n    }\n\n    if (type == StatementType.DO) {\n      continueSet.remove(first.getBasichead());\n    }\n\n    return continueSet;\n  }\n\n  public void buildMonitorFlags() {\n\n    for (Statement st : stats) {\n      st.buildMonitorFlags();\n    }\n\n    switch (type) {\n      case BASIC_BLOCK -> {\n        BasicBlockStatement bblock = (BasicBlockStatement)this;\n        InstructionSequence seq = bblock.getBlock().getSeq();\n\n        if (seq != null && !seq.isEmpty()) {\n          for (int i = 0; i < seq.length(); i++) {\n            if (seq.getInstr(i).opcode == CodeConstants.opc_monitorexit) {\n              containsMonitorExit = true;\n              break;\n            }\n          }\n          isMonitorEnter = (seq.getLastInstr().opcode == CodeConstants.opc_monitorenter);\n        }\n      }\n      case SEQUENCE, IF -> {\n        containsMonitorExit = false;\n        for (Statement st : stats) {\n          containsMonitorExit |= st.isContainsMonitorExit();\n        }\n      }\n      case SYNCHRONIZED, ROOT, GENERAL -> { }\n      default -> {\n        containsMonitorExit = false;\n        for (Statement st : stats) {\n          containsMonitorExit |= st.isContainsMonitorExit();\n        }\n      }\n    }\n  }\n\n\n  public List<Statement> getReversePostOrderList() {\n    return getReversePostOrderList(first);\n  }\n\n  public List<Statement> getReversePostOrderList(Statement stat) {\n    cancellationManager.checkCanceled();\n    List<Statement> res = new ArrayList<>();\n\n    addToReversePostOrderListIterative(stat, res);\n\n    return res;\n  }\n\n  public List<Statement> getPostReversePostOrderList() {\n    return getPostReversePostOrderList(null);\n  }\n\n  public List<Statement> getPostReversePostOrderList(List<Statement> lstexits) {\n    cancellationManager.checkCanceled();\n    List<Statement> res = new ArrayList<>();\n\n    if (lstexits == null) {\n      lstexits = new StrongConnectivityHelper(this).getExitReps();\n    }\n\n    HashSet<Statement> setVisited = new HashSet<>();\n\n    for (Statement exit : lstexits) {\n      cancellationManager.checkCanceled();\n      addToPostReversePostOrderList(exit, res, setVisited);\n    }\n\n    if (res.size() != stats.size()) {\n      throw new RuntimeException(\"computing post reverse post order failed!\");\n    }\n\n    return res;\n  }\n\n  public boolean containsStatement(Statement stat) {\n    return this == stat || containsStatementStrict(stat);\n  }\n\n  public boolean containsStatementStrict(Statement stat) {\n    cancellationManager.checkCanceled();\n\n    if (stats.contains(stat)) {\n      return true;\n    }\n\n    for (Statement st : stats) {\n      if (st.containsStatementStrict(stat)) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    throw new RuntimeException(\"not implemented\");\n  }\n\n  // TODO: make obsolete and remove\n  public List<Object> getSequentialObjects() {\n    return new ArrayList<>(stats);\n  }\n\n  public void initExprents() {\n    // do nothing\n  }\n\n  public void replaceExprent(Exprent oldexpr, Exprent newexpr) {\n    // do nothing\n  }\n\n  public Statement getSimpleCopy() {\n    throw new RuntimeException(\"not implemented\");\n  }\n\n  public void initSimpleCopy() {\n    if (!stats.isEmpty()) {\n      first = stats.get(0);\n    }\n  }\n\n  public void replaceStatement(Statement oldstat, Statement newstat) {\n    cancellationManager.checkCanceled();\n    for (StatEdge edge : oldstat.getAllPredecessorEdges()) {\n      oldstat.removePredecessor(edge);\n      edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, newstat);\n      newstat.addPredecessor(edge);\n    }\n\n    for (StatEdge edge : oldstat.getAllSuccessorEdges()) {\n      oldstat.removeSuccessor(edge);\n      edge.setSource(newstat);\n      newstat.addSuccessor(edge);\n    }\n\n    int statindex = stats.getIndexByKey(oldstat.id);\n    stats.removeWithKey(oldstat.id);\n    stats.addWithKeyAndIndex(statindex, newstat, newstat.id);\n\n    newstat.setParent(this);\n    newstat.post = oldstat.post;\n\n    if (first == oldstat) {\n      first = newstat;\n    }\n\n    List<StatEdge> lst = new ArrayList<>(oldstat.getLabelEdges());\n\n    for (int i = lst.size() - 1; i >= 0; i--) {\n      StatEdge edge = lst.get(i);\n      if (edge.getSource() != newstat) {\n        newstat.addLabeledEdge(edge);\n      }\n      else {\n        if (this == edge.getDestination() || this.containsStatementStrict(edge.getDestination())) {\n          edge.closure = null;\n        }\n        else {\n          this.addLabeledEdge(edge);\n        }\n      }\n    }\n\n    oldstat.getLabelEdges().clear();\n  }\n\n\n  // *****************************************************************************\n  // private methods\n  // *****************************************************************************\n\n  private static void addToReversePostOrderListIterative(Statement root, List<? super Statement> lst) {\n\n    LinkedList<Statement> stackNode = new LinkedList<>();\n    LinkedList<Integer> stackIndex = new LinkedList<>();\n    HashSet<Statement> setVisited = new HashSet<>();\n\n    stackNode.add(root);\n    stackIndex.add(0);\n\n    while (!stackNode.isEmpty()) {\n\n      Statement node = stackNode.getLast();\n      int index = stackIndex.removeLast();\n\n      setVisited.add(node);\n\n      List<StatEdge> lstEdges = node.getAllSuccessorEdges();\n\n      for (; index < lstEdges.size(); index++) {\n        StatEdge edge = lstEdges.get(index);\n        Statement succ = edge.getDestination();\n\n        if (!setVisited.contains(succ) &&\n            (edge.getType() == EdgeType.REGULAR || edge.getType() == EdgeType.EXCEPTION)) { // TODO: edge filter?\n\n          stackIndex.add(index + 1);\n\n          stackNode.add(succ);\n          stackIndex.add(0);\n\n          break;\n        }\n      }\n\n      if (index == lstEdges.size()) {\n        lst.add(0, node);\n\n        stackNode.removeLast();\n      }\n    }\n  }\n\n\n  private static void addToPostReversePostOrderList(Statement stat, List<? super Statement> lst, HashSet<? super Statement> setVisited) {\n\n    if (setVisited.contains(stat)) { // because of not considered exception edges, s. isExitComponent. Should be rewritten, if possible.\n      return;\n    }\n    setVisited.add(stat);\n\n    for (StatEdge prededge : stat.getEdges(EdgeType.REGULAR.unite(EdgeType.EXCEPTION), EdgeDirection.BACKWARD)) {\n      Statement pred = prededge.getSource();\n      if (!setVisited.contains(pred)) {\n        addToPostReversePostOrderList(pred, lst, setVisited);\n      }\n    }\n\n    lst.add(0, stat);\n  }\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public void changeEdgeNode(EdgeDirection direction, StatEdge edge, Statement value) {\n    cancellationManager.checkCanceled();\n\n    Map<EdgeType, List<StatEdge>> mapEdges = direction == EdgeDirection.BACKWARD ? mapPredEdges : mapSuccEdges;\n    Map<EdgeType, List<Statement>> mapStates = direction == EdgeDirection.BACKWARD ? mapPredStates : mapSuccStates;\n\n    EdgeType type = edge.getType();\n\n    EdgeType[] arrtypes;\n    if (type == EdgeType.EXCEPTION) {\n      arrtypes = new EdgeType[]{EdgeType.ALL, EdgeType.EXCEPTION};\n    }\n    else {\n      arrtypes = new EdgeType[]{EdgeType.ALL, EdgeType.DIRECT_ALL, type};\n    }\n\n    for (EdgeType edgetype : arrtypes) {\n      List<StatEdge> lst = mapEdges.get(edgetype);\n      if (lst != null) {\n        int index = lst.indexOf(edge);\n        if (index >= 0) {\n          mapStates.get(edgetype).set(index, value);\n        }\n      }\n    }\n\n    if (direction == EdgeDirection.BACKWARD) {\n      edge.setSource(value);\n    }\n    else {\n      edge.setDestination(value);\n    }\n  }\n\n  public void changeEdgeType(EdgeDirection direction, StatEdge edge, EdgeType newtype) {\n    cancellationManager.checkCanceled();\n\n    EdgeType oldtype = edge.getType();\n    if (oldtype == newtype) {\n      return;\n    }\n\n    if (oldtype == EdgeType.EXCEPTION || newtype == EdgeType.EXCEPTION) {\n      throw new RuntimeException(\"Invalid edge type!\");\n    }\n\n    removeEdgeDirectInternal(direction, edge, oldtype);\n    addEdgeDirectInternal(direction, edge, newtype);\n\n    if (direction == EdgeDirection.FORWARD) {\n      edge.getDestination().changeEdgeType(EdgeDirection.BACKWARD, edge, newtype);\n    }\n\n    edge.setType(newtype);\n  }\n\n\n  private List<StatEdge> getEdges(EdgeType type, @NotNull EdgeDirection direction) {\n    cancellationManager.checkCanceled();\n\n    Map<EdgeType, List<StatEdge>> map = direction == EdgeDirection.BACKWARD ? mapPredEdges : mapSuccEdges;\n\n    List<StatEdge> res;\n    if ((type.mask() & (type.mask() - 1)) == 0) {\n      res = map.get(type);\n      res = res == null ? new ArrayList<>() : new ArrayList<>(res);\n    }\n    else {\n      res = new ArrayList<>();\n      for (EdgeType edgetype : EdgeType.types()) {\n        if ((type.mask() & edgetype.mask()) != 0) {\n          List<StatEdge> lst = map.get(edgetype);\n          if (lst != null) {\n            res.addAll(lst);\n          }\n        }\n      }\n    }\n\n    return res;\n  }\n\n  public List<Statement> getNeighbours(EdgeType type, EdgeDirection direction) {\n    cancellationManager.checkCanceled();\n\n    Map<EdgeType, List<Statement>> map = direction == EdgeDirection.BACKWARD ? mapPredStates : mapSuccStates;\n\n    List<Statement> res;\n    if ((type.mask() & (type.mask() - 1)) == 0) {\n      res = map.get(type);\n      res = res == null ? new ArrayList<>() : new ArrayList<>(res);\n    }\n    else {\n      res = new ArrayList<>();\n      for (EdgeType edgetype : EdgeType.types()) {\n        if ((type.mask() & edgetype.mask()) != 0) {\n          List<Statement> lst = map.get(edgetype);\n          if (lst != null) {\n            res.addAll(lst);\n          }\n        }\n      }\n    }\n\n    return res;\n  }\n\n  public Set<Statement> getNeighboursSet(EdgeType type, EdgeDirection direction) {\n    return new HashSet<>(getNeighbours(type, direction));\n  }\n\n  public List<StatEdge> getSuccessorEdges(EdgeType type) {\n    return getEdges(type, EdgeDirection.FORWARD);\n  }\n\n  public List<StatEdge> getPredecessorEdges(EdgeType type) {\n    return getEdges(type, EdgeDirection.BACKWARD);\n  }\n\n  public List<StatEdge> getAllSuccessorEdges() {\n    return getEdges(EdgeType.ALL, EdgeDirection.FORWARD);\n  }\n\n  public List<StatEdge> getAllPredecessorEdges() {\n    return getEdges(EdgeType.ALL, EdgeDirection.BACKWARD);\n  }\n\n  public Statement getFirst() {\n    cancellationManager.checkCanceled();\n    return first;\n  }\n\n  public void setFirst(Statement first) {\n    this.first = first;\n  }\n\n  public Statement getPost() {\n    return post;\n  }\n\n  public VBStyleCollection<Statement, Integer> getStats() {\n    cancellationManager.checkCanceled();\n    return stats;\n  }\n\n  public StatementType getLastBasicType() {\n    return lastBasicType;\n  }\n\n  public HashSet<Statement> getContinueSet() {\n    return continueSet;\n  }\n\n  public boolean isContainsMonitorExit() {\n    return containsMonitorExit;\n  }\n\n  public boolean isMonitorEnter() {\n    return isMonitorEnter;\n  }\n\n  public BasicBlockStatement getBasichead() {\n    if (type == StatementType.BASIC_BLOCK) {\n      return (BasicBlockStatement)this;\n    }\n    else {\n      return first.getBasichead();\n    }\n  }\n\n  public boolean isLabeled() {\n\n    for (StatEdge edge : labelEdges) {\n      if (edge.labeled && edge.explicit) {  // FIXME: consistent setting\n        return true;\n      }\n    }\n    return false;\n  }\n\n  public boolean hasBasicSuccEdge() {\n\n    // FIXME: default switch\n\n    return type == StatementType.BASIC_BLOCK || (type == StatementType.IF &&\n                                                        ((IfStatement)this).iftype == IfStatement.IFTYPE_IF) ||\n           (type == StatementType.DO && ((DoStatement)this).getLoopType() != LoopType.DO);\n  }\n\n\n  public Statement getParent() {\n    cancellationManager.checkCanceled();\n    return parent;\n  }\n\n  public void setParent(Statement parent) {\n    this.parent = parent;\n  }\n\n  public HashSet<StatEdge> getLabelEdges() {  // FIXME: why HashSet?\n    return labelEdges;\n  }\n\n  public List<Exprent> getVarDefinitions() {\n    cancellationManager.checkCanceled();\n    return varDefinitions;\n  }\n\n  @Nullable\n  public List<Exprent> getExprents() {\n    cancellationManager.checkCanceled();\n    return exprents;\n  }\n\n  public void setExprents(List<Exprent> exprents) {\n    this.exprents = exprents;\n  }\n\n  public boolean isCopied() {\n    return copied;\n  }\n\n  public void setCopied(boolean copied) {\n    this.copied = copied;\n  }\n\n  // helper methods\n  public String toString() {\n    return Integer.toString(id);\n  }\n\n  // *****************************************************************************\n  // IMatchable implementation\n  // *****************************************************************************\n\n  @Override\n  public IMatchable findObject(MatchNode matchNode, int index) {\n    int node_type = matchNode.getType();\n\n    if (node_type == MatchNode.MATCHNODE_STATEMENT && !this.stats.isEmpty()) {\n      String position = (String)matchNode.getRuleValue(MatchProperties.STATEMENT_POSITION);\n      if (position != null) {\n        if (position.matches(\"-?\\\\d+\")) {\n          return this.stats.get((this.stats.size() + Integer.parseInt(position)) % this.stats.size()); // care for negative positions\n        }\n      }\n      else if (index < this.stats.size()) { // use 'index' parameter\n        return this.stats.get(index);\n      }\n    }\n    else if (node_type == MatchNode.MATCHNODE_EXPRENT && this.exprents != null && !this.exprents.isEmpty()) {\n      String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);\n      if (position != null) {\n        if (position.matches(\"-?\\\\d+\")) {\n          return this.exprents.get((this.exprents.size() + Integer.parseInt(position)) % this.exprents.size()); // care for negative positions\n        }\n      }\n      else if (index < this.exprents.size()) { // use 'index' parameter\n        return this.exprents.get(index);\n      }\n    }\n\n    return null;\n  }\n\n  @Override\n  public boolean match(MatchNode matchNode, MatchEngine engine) {\n    if (matchNode.getType() != MatchNode.MATCHNODE_STATEMENT) {\n      return false;\n    }\n\n    for (Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {\n      switch (rule.getKey()) {\n        case STATEMENT_TYPE -> {\n          if (this.type != rule.getValue().value) {\n            return false;\n          }\n        }\n        case STATEMENT_STATSIZE -> {\n          if (this.stats.size() != (Integer)rule.getValue().value) {\n            return false;\n          }\n        }\n        case STATEMENT_EXPRSIZE -> {\n          int exprsize = (Integer)rule.getValue().value;\n          if (exprsize == -1) {\n            if (this.exprents != null) {\n              return false;\n            }\n          }\n          else {\n            if (this.exprents == null || this.exprents.size() != exprsize) {\n              return false;\n            }\n          }\n        }\n        case STATEMENT_RET -> {\n          if (!engine.checkAndSetVariableValue((String)rule.getValue().value, this)) {\n            return false;\n          }\n        }\n      }\n    }\n\n    return true;\n  }\n\n  public enum StatementType {\n    GENERAL,\n    IF,\n    DO,\n    SWITCH,\n    TRY_CATCH,\n    BASIC_BLOCK,\n    // FINALLY,\n    SYNCHRONIZED,\n    PLACEHOLDER,\n    CATCH_ALL,\n    ROOT,\n    DUMMY_EXIT,\n    SEQUENCE\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statements.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.main.rels.ClassWrapper;\nimport org.jetbrains.java.decompiler.main.rels.MethodWrapper;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;\n\npublic final class Statements {\n  public static Statement findFirstData(Statement stat) {\n    if (stat.getExprents() != null) {\n      return stat;\n    }\n    else if (stat.isLabeled()) { // FIXME: Why??\n      return null;\n    }\n\n    return switch (stat.type) {\n      case SEQUENCE, IF, ROOT, SWITCH, SYNCHRONIZED -> findFirstData(stat.getFirst());\n      default -> null;\n    };\n  }\n\n  public static boolean isInvocationInitConstructor(InvocationExprent inv, MethodWrapper method, ClassWrapper wrapper, boolean withThis) {\n    if (inv.getFuncType() == InvocationExprent.TYPE_INIT && inv.getInstance().type == Exprent.EXPRENT_VAR) {\n      VarExprent instVar = (VarExprent)inv.getInstance();\n      VarVersionPair varPair = new VarVersionPair(instVar);\n      String className = method.varproc.getThisVars().get(varPair);\n      if (className != null) { // any this instance. TODO: Restrict to current class?\n        return withThis || !wrapper.getClassStruct().qualifiedName.equals(inv.getClassName());\n      }\n    }\n\n    return false;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.code.SwitchInstruction;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.DecHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeDirection;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.SwitchExprent;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.*;\n\nimport static org.jetbrains.java.decompiler.ClassNameConstants.JAVA_LANG_CHARACTER;\nimport static org.jetbrains.java.decompiler.code.CodeConstants.TYPE_OBJECT;\nimport static org.jetbrains.java.decompiler.struct.gen.VarType.VARTYPE_CHAR;\n\npublic final class SwitchStatement extends Statement {\n  private List<Statement> caseStatements = new ArrayList<>();\n  private List<List<StatEdge>> caseEdges = new ArrayList<>();\n  private List<List<@Nullable Exprent>> caseValues = new ArrayList<>();\n  private final Map<Statement, Exprent> guards = new HashMap<>();\n  private StatEdge defaultEdge;\n  private Exprent headExprent;\n  private boolean canBeRule = false;\n  private boolean useCustomDefault = false;\n\n  private SwitchStatement() {\n    super(StatementType.SWITCH);\n  }\n\n  private SwitchStatement(@NotNull Statement head, @Nullable Statement postStatement) {\n    this();\n    first = head;\n    stats.addWithKey(head, head.id);\n    // find post node\n    Set<Statement> regularSuccessors = new HashSet<>(head.getNeighbours(EdgeType.REGULAR, EdgeDirection.FORWARD));\n    // cluster nodes\n    if (postStatement != null) {\n      post = postStatement;\n      regularSuccessors.remove(post);\n    }\n    defaultEdge = head.getSuccessorEdges(EdgeType.DIRECT_ALL).get(0);\n    for (Statement successor : regularSuccessors) {\n      stats.addWithKey(successor, successor.id);\n    }\n  }\n\n  public void setCanBeRule(boolean canBeRule) {\n    this.canBeRule = canBeRule;\n  }\n\n  public void addGuard(@NotNull Statement statement, @NotNull Exprent guard) {\n    guards.put(statement, guard);\n  }\n\n\n  /**\n   * Removes the specified case statement from the switch statement.\n   *\n   * @param statement the statement to be removed\n   */\n  public void removeCaseStatement(@NotNull Statement statement) {\n    stats.removeWithKey(statement.id);\n    int caseIndex = caseStatements.indexOf(statement);\n    if (caseIndex < 0) {\n      return;\n    }\n    caseStatements.remove(caseIndex);\n    caseEdges.remove(caseIndex);\n    caseValues.remove(caseIndex);\n    for (StatEdge edge : statement.getAllSuccessorEdges()) {\n      edge.getDestination().removePredecessor(edge);\n    }\n    for (StatEdge edge : statement.getAllPredecessorEdges()) {\n      edge.getSource().removeSuccessor(edge);\n    }\n  }\n\n\n  /**\n   * Duplicates a case statement within a switch statement.\n   * Labels are not copied\n   *\n   * @param currentStatement The current case statement to duplicate.\n   * @return The index of the duplicated case statement.\n   *\n   */\n  public int duplicateCaseStatement(@NotNull Statement currentStatement) {\n    Statement dummy = currentStatement.getSimpleCopy();\n    int statIndex = stats.indexOf(currentStatement);\n    if (statIndex < 0) {\n      return statIndex;\n    }\n    stats.addWithKeyAndIndex(statIndex + 1, dummy, dummy.id);\n\n    int caseIndex = caseStatements.indexOf(currentStatement);\n    caseStatements.add(caseIndex + 1, dummy);\n    List<@Nullable Exprent> toCopyValues = caseValues.get(caseIndex);\n    caseValues.add(caseIndex + 1, toCopyValues);\n    List<StatEdge> previousEdges = caseEdges.get(caseIndex);\n    List<StatEdge> toCopyEdges = new ArrayList<>();\n    for (StatEdge previousEdge : previousEdges) {\n      StatEdge edge = previousEdge.copy();\n      edge.setDestination(dummy);\n      toCopyEdges.add(edge);\n    }\n    caseEdges.add(caseIndex + 1, toCopyEdges);\n\n    for (StatEdge edge : currentStatement.getAllPredecessorEdges()) {\n      StatEdge copy = edge.copy();\n      copy.setDestination(dummy);\n      copy.getSource().addSuccessor(copy);\n    }\n\n    for (StatEdge edge : currentStatement.getAllSuccessorEdges()) {\n      StatEdge copy = edge.copy();\n      copy.setSource(dummy);\n      dummy.addSuccessor(copy);\n    }\n\n    dummy.setParent(this);\n    return caseIndex + 1;\n  }\n\n  public void setUseCustomDefault() {\n    useCustomDefault = true;\n  }\n\n  @Nullable\n  public static Statement isHead(@NotNull Statement head) {\n    if (head.type == StatementType.BASIC_BLOCK && head.getLastBasicType() == StatementType.SWITCH) {\n      List<Statement> statements = new ArrayList<>();\n      if (DecHelper.isChoiceStatement(head, statements)) {\n        Statement post = statements.remove(0);\n        for (Statement statement : statements) {\n          if (statement.isMonitorEnter()) {\n            return null;\n          }\n        }\n        if (DecHelper.checkStatementExceptions(statements)) {\n          return new SwitchStatement(head, post);\n        }\n      }\n    }\n    return null;\n  }\n\n  @Override\n  @NotNull\n  public TextBuffer toJava(int indent, @NotNull BytecodeMappingTracer tracer) {\n    SwitchHelper.simplifySwitchOnEnum(this);\n    TextBuffer buf = new TextBuffer();\n    buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer));\n    buf.append(first.toJava(indent, tracer));\n    if (isLabeled()) {\n      buf.appendIndent(indent).append(\"label\").append(Integer.toString(id)).append(\":\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n    buf.appendIndent(indent).append(headExprent.toJava(indent, tracer)).append(\" {\").appendLineSeparator();\n    tracer.incrementCurrentSourceLine();\n    VarType switchType = headExprent.getExprType();\n    for (int i = 0; i < caseStatements.size(); i++) {\n      Statement stat = caseStatements.get(i);\n      List<StatEdge> edges = caseEdges.get(i);\n      List<Exprent> values = caseValues.get(i);\n      for (int j = 0; j < edges.size(); j++) {\n        if (edges.get(j) == defaultEdge && !useCustomDefault) {\n          if (!canBeRule) {\n            buf.appendIndent(indent + 1).append(\"default:\").appendLineSeparator();\n          }\n          else {\n            buf.appendIndent(indent + 1).append(\"default -> \");\n          }\n        }\n        else {\n          buf.appendIndent(indent + 1).append(\"case \");\n          Exprent value = values.get(j);\n          if (value instanceof ConstExprent constExprent && !constExprent.isNull()) {\n            value = value.copy();\n            if (switchType.getType() != TYPE_OBJECT) {\n              ((ConstExprent)value).setConstType(switchType);\n            }\n            else if (((JAVA_LANG_CHARACTER).equals(switchType.getValue()))) {\n              ((ConstExprent)value).setConstType(VARTYPE_CHAR);\n            }\n          }\n          if (value instanceof FieldExprent && ((FieldExprent)value).isStatic()) { // enum values\n            buf.append(((FieldExprent)value).getName());\n          }\n          else {\n            buf.append(value.toJava(indent, tracer));\n          }\n\n          Exprent guard = guards.get(stat);\n          if (guard != null) {\n            buf.append(\" when \").append(guard.toJava(0, tracer));\n          }\n\n          if (!canBeRule) {\n            buf.append(\":\").appendLineSeparator();\n          }\n          else {\n            buf.append(\" -> \");\n          }\n        }\n        if (!canBeRule) {\n          tracer.incrementCurrentSourceLine();\n        }\n      }\n      //example:\n      //case 0: break\n      if (canBeRule && stat instanceof BasicBlockStatement blockStatement && blockStatement.getBlock().getSeq().isEmpty() &&\n          (stat.getExprents() == null || stat.getExprents().isEmpty())) {\n        buf.append(\"{ }\").appendLineSeparator();\n        tracer.incrementCurrentSourceLine();\n      }\n      else {\n        buf.append(ExprProcessor.jmpWrapper(stat, canBeRule ? 0 : indent + 2, false, tracer));\n      }\n    }\n    buf.appendIndent(indent).append(\"}\").appendLineSeparator();\n    tracer.incrementCurrentSourceLine();\n    return buf;\n  }\n\n  @Override\n  public void initExprents() {\n    SwitchExprent exprent = (SwitchExprent)first.getExprents().remove(first.getExprents().size() - 1);\n    exprent.setCaseValues(caseValues);\n    headExprent = exprent;\n  }\n\n  @Override\n  @NotNull\n  public List<Object> getSequentialObjects() {\n    List<Object> result = new ArrayList<>(stats);\n    result.add(1, headExprent);\n    return result;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldExprent, Exprent newExprent) {\n    if (headExprent == oldExprent) {\n      headExprent = newExprent;\n    }\n  }\n\n  @Override\n  public void replaceStatement(Statement oldStatement, Statement newStatement) {\n    for (int i = 0; i < caseStatements.size(); i++) {\n      if (caseStatements.get(i) == oldStatement) {\n        caseStatements.set(i, newStatement);\n      }\n    }\n    super.replaceStatement(oldStatement, newStatement);\n  }\n\n  @Override\n  @NotNull\n  public Statement getSimpleCopy() {\n    return new SwitchStatement();\n  }\n\n  @Override\n  public void initSimpleCopy() {\n    first = stats.get(0);\n    defaultEdge = first.getSuccessorEdges(EdgeType.DIRECT_ALL).get(0);\n    sortEdgesAndNodes();\n  }\n\n  public void sortEdgesAndNodes() {\n    Map<StatEdge, Integer> edgeIndicesMapping = new HashMap<>();\n    List<StatEdge> firstSuccessors = first.getSuccessorEdges(EdgeType.DIRECT_ALL);\n    for (int i = 0; i < firstSuccessors.size(); i++) {\n      edgeIndicesMapping.put(firstSuccessors.get(i), i == 0 ? firstSuccessors.size() : i);\n    }\n\n    BasicBlockStatement firstBlock = (BasicBlockStatement)first;\n    int[] values = ((SwitchInstruction)firstBlock.getBlock().getLastInstruction()).getValues();\n    List<@Nullable Statement> caseStatements = new ArrayList<>(stats.size() - 1);\n    List<List<Integer>> edgeIndices = new ArrayList<>(stats.size() - 1);\n    collectRegularEdgesIndices(edgeIndicesMapping, caseStatements, edgeIndices);\n    collectExitEdgesIndices(edgeIndicesMapping, caseStatements, edgeIndices);\n    sortEdges(caseStatements, edgeIndices);\n\n    List<List<StatEdge>> caseEdges = new ArrayList<>(edgeIndices.size());\n    List<List<@Nullable Exprent>> caseValues = new ArrayList<>(edgeIndices.size());\n    mapEdgeIndicesToEdges(values, edgeIndices, caseEdges, caseValues);\n\n    replaceNullStatementsWithBasicBlocks(caseStatements, caseEdges);\n\n    this.caseStatements = caseStatements;\n    this.caseEdges = caseEdges;\n    this.caseValues = caseValues;\n  }\n\n  private void mapEdgeIndicesToEdges(int[] values,\n                                     @NotNull List<List<Integer>> edgeIndices,\n                                     @NotNull List<List<StatEdge>> caseEdges,\n                                     @NotNull List<List<@Nullable Exprent>> caseValues) {\n    for (List<Integer> indices : edgeIndices) {\n      List<StatEdge> edges = new ArrayList<>(indices.size());\n      List<Exprent> valueExprents = new ArrayList<>(indices.size());\n      List<StatEdge> firstSuccessors = first.getSuccessorEdges(EdgeType.DIRECT_ALL);\n      for (Integer in : indices) {\n        int index = in == firstSuccessors.size() ? 0 : in;\n        edges.add(firstSuccessors.get(index));\n        valueExprents.add(index == 0 ? null : new ConstExprent(values[index - 1], false, null));\n      }\n      caseEdges.add(edges);\n      caseValues.add(valueExprents);\n    }\n  }\n\n  private void collectRegularEdgesIndices(@NotNull Map<StatEdge, Integer> edgeIndicesMapping,\n                                          @NotNull List<@Nullable Statement> nodes,\n                                          @NotNull List<List<Integer>> edgeIndices) {\n    for (int i = 1; i < stats.size(); i++) {\n      Statement statement = stats.get(i);\n      List<Integer> regularEdgeIndices = new ArrayList<>();\n      for (StatEdge regularEdge : statement.getPredecessorEdges(EdgeType.REGULAR)) {\n        if (regularEdge.getSource() == first) {\n          regularEdgeIndices.add(edgeIndicesMapping.get(regularEdge));\n        }\n      }\n      Collections.sort(regularEdgeIndices);\n      nodes.add(statement);\n      edgeIndices.add(regularEdgeIndices);\n    }\n  }\n\n  private void collectExitEdgesIndices(@NotNull Map<StatEdge, Integer> edgeIndicesMapping,\n                                       @NotNull List<@Nullable Statement> nodes,\n                                       @NotNull List<List<Integer>> edgeIndices) {\n    List<StatEdge> firstExitEdges = first.getSuccessorEdges(EdgeType.BREAK.unite(EdgeType.CONTINUE));\n    while (!firstExitEdges.isEmpty()) {\n      StatEdge exitEdge = firstExitEdges.get(0);\n      List<Integer> exitEdgeIndices = new ArrayList<>();\n      for (int i = firstExitEdges.size() - 1; i >= 0; i--) {\n        StatEdge edgeTemp = firstExitEdges.get(i);\n        if (edgeTemp.getDestination() == exitEdge.getDestination() && edgeTemp.getType() == exitEdge.getType()) {\n          exitEdgeIndices.add(edgeIndicesMapping.get(edgeTemp));\n          firstExitEdges.remove(i);\n        }\n      }\n      Collections.sort(exitEdgeIndices);\n      nodes.add(null);\n      edgeIndices.add(exitEdgeIndices);\n    }\n  }\n\n  private void sortEdges(List<@Nullable Statement> nodes, @NotNull List<List<Integer>> edgeIndices) {\n    for (int i = 0; i < edgeIndices.size() - 1; i++) {\n      for (int j = edgeIndices.size() - 1; j > i; j--) {\n        if (edgeIndices.get(j - 1).get(0) > edgeIndices.get(j).get(0)) {\n          edgeIndices.set(j, edgeIndices.set(j - 1, edgeIndices.get(j)));\n          nodes.set(j, nodes.set(j - 1, nodes.get(j)));\n        }\n      }\n    }\n    for (int index = 0; index < nodes.size(); index++) {\n      Statement node = nodes.get(index);\n      if (node == null) continue;\n      HashSet<Statement> nodePredecessors = new HashSet<>(node.getNeighbours(EdgeType.REGULAR, EdgeDirection.BACKWARD));\n      nodePredecessors.remove(first);\n      if (nodePredecessors.isEmpty()) continue;\n      // assumption: at most one predecessor node besides the head. May not hold true for obfuscated code.\n      Statement predecessor = nodePredecessors.iterator().next();\n      for (int j = 0; j < nodes.size(); j++) {\n        if (j != (index - 1) && nodes.get(j) == predecessor) {\n          nodes.add(j + 1, node);\n          edgeIndices.add(j + 1, edgeIndices.get(index));\n          if (j > index) {\n            nodes.remove(index);\n            edgeIndices.remove(index);\n            index--;\n          }\n          else {\n            nodes.remove(index + 1);\n            edgeIndices.remove(index + 1);\n          }\n          break;\n        }\n      }\n    }\n  }\n\n  private void replaceNullStatementsWithBasicBlocks(List<@Nullable Statement> statements, @NotNull List<List<StatEdge>> edges) {\n    for (int i = 0; i < statements.size(); i++) {\n      if (statements.get(i) == null) {\n        BasicBlockStatement basicBlock = new BasicBlockStatement(new BasicBlock(\n          DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));\n        StatEdge sampleEdge = edges.get(i).get(0);\n        basicBlock.addSuccessor(new StatEdge(sampleEdge.getType(), basicBlock, sampleEdge.getDestination(), sampleEdge.closure));\n        for (StatEdge edge : edges.get(i)) {\n          edge.getSource().changeEdgeType(EdgeDirection.FORWARD, edge, EdgeType.REGULAR);\n          edge.closure.getLabelEdges().remove(edge);\n          edge.getDestination().removePredecessor(edge);\n          edge.getSource().changeEdgeNode(EdgeDirection.FORWARD, edge, basicBlock);\n          basicBlock.addPredecessor(edge);\n        }\n        statements.set(i, basicBlock);\n        stats.addWithKey(basicBlock, basicBlock.id);\n        basicBlock.setParent(this);\n      }\n    }\n  }\n\n  @NotNull\n  public List<Exprent> getHeadExprentList() {\n    return Collections.singletonList(headExprent);\n  }\n\n  @Nullable\n  public Exprent getHeadExprent() {\n    return headExprent;\n  }\n\n  @NotNull\n  public List<List<StatEdge>> getCaseEdges() {\n    return caseEdges;\n  }\n\n  @NotNull\n  public List<Statement> getCaseStatements() {\n    return caseStatements;\n  }\n\n  @NotNull\n  public StatEdge getDefaultEdge() {\n    return defaultEdge;\n  }\n\n  @NotNull\n  public List<List<@Nullable Exprent>> getCaseValues() {\n    return caseValues;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.stats;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.code.cfg.BasicBlock;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.SequenceHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge;\nimport org.jetbrains.java.decompiler.modules.decompiler.StatEdge.EdgeType;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\npublic class SynchronizedStatement extends Statement {\n\n  private Statement body;\n\n  private final List<Exprent> headexprent = new ArrayList<>(1);\n\n  // *****************************************************************************\n  // constructors\n  // *****************************************************************************\n\n  public SynchronizedStatement() {\n    super(StatementType.SYNCHRONIZED);\n\n    headexprent.add(null);\n  }\n\n  public SynchronizedStatement(Statement head, Statement body, Statement exc) {\n\n    this();\n\n    first = head;\n    stats.addWithKey(head, head.id);\n\n    this.body = body;\n    stats.addWithKey(body, body.id);\n\n    stats.addWithKey(exc, exc.id);\n\n    List<StatEdge> lstSuccs = body.getSuccessorEdges(EdgeType.DIRECT_ALL);\n    if (!lstSuccs.isEmpty()) {\n      StatEdge edge = lstSuccs.get(0);\n      if (edge.getType() == EdgeType.REGULAR) {\n        post = edge.getDestination();\n      }\n    }\n  }\n\n\n  // *****************************************************************************\n  // public methods\n  // *****************************************************************************\n\n  @Override\n  public TextBuffer toJava(int indent, BytecodeMappingTracer tracer) {\n    TextBuffer buf = new TextBuffer();\n    buf.append(ExprProcessor.listToJava(varDefinitions, indent, tracer));\n    buf.append(first.toJava(indent, tracer));\n\n    if (isLabeled()) {\n      buf.appendIndent(indent).append(\"label\").append(Integer.toString(id)).append(\":\").appendLineSeparator();\n      tracer.incrementCurrentSourceLine();\n    }\n\n    buf.appendIndent(indent).append(headexprent.get(0).toJava(indent, tracer)).append(\" {\").appendLineSeparator();\n    tracer.incrementCurrentSourceLine();\n\n    buf.append(ExprProcessor.jmpWrapper(body, indent + 1, true, tracer));\n\n    buf.appendIndent(indent).append(\"}\").appendLineSeparator();\n    mapMonitorExitInstr(tracer);\n    tracer.incrementCurrentSourceLine();\n\n    return buf;\n  }\n\n  private void mapMonitorExitInstr(BytecodeMappingTracer tracer) {\n    BasicBlock block = body.getBasichead().getBlock();\n    if (!block.getSeq().isEmpty() && block.getLastInstruction().opcode == CodeConstants.opc_monitorexit) {\n      Integer offset = block.getOriginalOffset(block.size() - 1);\n      if (offset > -1) tracer.addMapping(offset);\n    }\n  }\n\n  @Override\n  public void initExprents() {\n    headexprent.set(0, first.getExprents().remove(first.getExprents().size() - 1));\n  }\n\n  @Override\n  public List<Object> getSequentialObjects() {\n\n    List<Object> lst = new ArrayList<>(stats);\n    lst.add(1, headexprent.get(0));\n\n    return lst;\n  }\n\n  @Override\n  public void replaceExprent(Exprent oldexpr, Exprent newexpr) {\n    if (headexprent.get(0) == oldexpr) {\n      headexprent.set(0, newexpr);\n    }\n  }\n\n  @Override\n  public void replaceStatement(Statement oldstat, Statement newstat) {\n\n    if (body == oldstat) {\n      body = newstat;\n    }\n\n    super.replaceStatement(oldstat, newstat);\n  }\n\n  public void removeExc() {\n    Statement exc = stats.get(2);\n    SequenceHelper.destroyStatementContent(exc, true);\n\n    stats.removeWithKey(exc.id);\n  }\n\n  @Override\n  public Statement getSimpleCopy() {\n    return new SynchronizedStatement();\n  }\n\n  @Override\n  public void initSimpleCopy() {\n    first = stats.get(0);\n    body = stats.get(1);\n  }\n\n  // *****************************************************************************\n  // getter and setter methods\n  // *****************************************************************************\n\n  public Statement getBody() {\n    return body;\n  }\n\n  public List<Exprent> getHeadexprentList() {\n    return headexprent;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/typeann/TargetInfo.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.typeann;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * Indicates the location of type annotations, retrieved from the type annotation attribute.\n * @see <a href=\"https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html\">The JVM class File Format Spec</a> Section 4.7.20.1\n */\npublic interface TargetInfo {\n  /**\n   * An item indicating that an annotation appears on the i'th type in an exception parameter declaration.\n   */\n  class CatchTarget implements TargetInfo {\n    private final int exceptionTableIndex;\n\n    public CatchTarget(int exceptionTableIndex) {\n      this.exceptionTableIndex = exceptionTableIndex;\n    }\n\n    /**\n     * @return An index indicating an item into the exception table of the code attribute.\n     */\n    public int getExceptionTableIndex() {\n      return exceptionTableIndex;\n    }\n  }\n\n  /**\n   * An item indicating that an annotation appears on either the type in a field declaration, the type in a record component declaration,\n   * the return type of a method, the type of a newly constructed object, or the receiver type of a method or constructor.\n   */\n  class EmptyTarget implements TargetInfo {\n    /**\n     * @return All empty target type annotations from a {@link TypeAnnotation} list.\n     */\n    public static List<TypeAnnotation> extract(List<TypeAnnotation> typeAnnotations) {\n      return typeAnnotations.stream()\n        .filter(typeAnnotation -> typeAnnotation.getTargetInfo() instanceof EmptyTarget)\n        .collect(Collectors.toList());\n    }\n  }\n\n  /**\n   * An item indicating that an annotation appears on the type in a formal parameter declaration of a method, constructor, or\n   * lambda expression.\n   */\n  class FormalParameterTarget implements TargetInfo {\n    private final int formalParameterIndex;\n\n    public FormalParameterTarget(int formalParameterIndex) {\n      this.formalParameterIndex = formalParameterIndex;\n    }\n\n    /**\n     * @return An index indicating which formal parameter declaration has an annotated type. The index starts at 0.\n     */\n    public int getFormalParameterIndex() {\n      return formalParameterIndex;\n    }\n\n    /**\n     * @return All formal parameter type annotations from a {@link TypeAnnotation} list.\n     */\n    public static List<TypeAnnotation> extract(List<TypeAnnotation> typeAnnotations) {\n      return typeAnnotations.stream()\n        .filter(typeAnnotation -> typeAnnotation.getTargetInfo() instanceof FormalParameterTarget)\n        .collect(Collectors.toList());\n    }\n\n    /**\n     * @return All formal parameter target annotations from a {@link TypeAnnotation} list at a specified index.\n     */\n    public static List<TypeAnnotation> extract(List<TypeAnnotation> typeAnnotations, int formalParameterIndex) {\n      return typeAnnotations.stream()\n        .filter(typeAnnotation -> {\n          TargetInfo targetInfo = typeAnnotation.getTargetInfo();\n          return targetInfo instanceof FormalParameterTarget\n                 && ((FormalParameterTarget)targetInfo).getFormalParameterIndex() == formalParameterIndex;\n        }).collect(Collectors.toList());\n    }\n  }\n\n  /**\n   * An item indicating that an annotation appears on the type in a local variable declaration, including a variable declared as a resource\n   * in a try-with-resources statement.\n   */\n  class LocalvarTarget implements TargetInfo {\n    private final Offsets[] table;\n\n    public LocalvarTarget(Offsets[] table) {\n      this.table = table;\n    }\n\n    /**\n     * @return A table containing the offsets of all local variables.\n     */\n    public Offsets[] getTable() {\n      return table;\n    }\n\n    /**\n     * Offset entry in the local var table.\n     */\n    public static class Offsets {\n      private final int startPc;\n\n      private final int length;\n\n      private final int index;\n\n      public Offsets(int startPc, int length, int index) {\n        this.startPc = startPc;\n        this.length = length;\n        this.index = index;\n      }\n\n      /**\n       * @return The start of the code array interval.\n       */\n      public int getStartPc() {\n        return startPc;\n      }\n\n      /**\n       * @return The length array of the code array interval.\n       */\n      public int getLength() {\n        return length;\n      }\n\n      /**\n       * @return The given local variable must be at index in the local variable array of the current frame.\n       */\n      public int getIndex() {\n        return index;\n      }\n    }\n  }\n\n  /**\n   * Item that indicates that an annotation appears on either the type in an instanceof expression or a new expression, or the type before\n   * the :: in a method reference expression.\n   */\n  class OffsetTarget implements TargetInfo {\n    private final int offset;\n\n    public OffsetTarget(int offset) {\n      this.offset = offset;\n    }\n\n    /**\n     * @return The offset item, specifying the code array offset of either the bytecode instruction corresponding to the instanceof\n     * expression, the new bytecode instruction corresponding to the new expression, or the bytecode instruction corresponding to\n     * the method reference expression.\n     */\n    public int getOffset() {\n      return offset;\n    }\n  }\n\n  /**\n   * Item that indicates that an annotation appears on a type in the extends or implements clause of a class or interface declaration. The\n   * index starts at 0.\n   */\n  class SupertypeTarget implements TargetInfo {\n    private static final int EXTENDS_CLAUSE_INDEX = 0xFFFF;\n\n    private final int supertypeIndex;\n\n    public SupertypeTarget(int supertypeIndex) {\n      this.supertypeIndex = supertypeIndex;\n    }\n\n    /**\n     * @return An index into the interfaces array of the enclosing ClassFile structure, which specifies that the annotation appears on that\n     * superinterface in either the implements clause of a class declaration or the extends clause of an interface declaration. A value of\n     * 65535 specifies that the annotation appears on the superclass in an extends clause of a class declaration.\n     */\n    public int getSupertypeIndex() {\n      return supertypeIndex;\n    }\n\n    public boolean inExtendsClause() {\n      return supertypeIndex == EXTENDS_CLAUSE_INDEX;\n    }\n\n    /**\n     * @return All super types annotations from a {@link TypeAnnotation} list at a specified super type index.\n     */\n    public static List<TypeAnnotation> extract(List<TypeAnnotation> typeAnnotations, int superTypeIndex) {\n      return typeAnnotations.stream()\n        .filter(typeAnnotation -> {\n          TargetInfo targetInfo = typeAnnotation.getTargetInfo();\n          return targetInfo instanceof SupertypeTarget && ((SupertypeTarget)targetInfo).getSupertypeIndex() == superTypeIndex;\n        }).collect(Collectors.toList());\n    }\n\n    /**\n     * @return The extended type annotations from a {@link TypeAnnotation} list.\n     */\n    public static List<TypeAnnotation> extractExtends(List<TypeAnnotation> typeAnnotations) {\n      return typeAnnotations.stream()\n        .filter(typeAnnotation -> {\n          TargetInfo targetInfo = typeAnnotation.getTargetInfo();\n          return targetInfo instanceof SupertypeTarget && ((SupertypeTarget)targetInfo).inExtendsClause();\n        }).collect(Collectors.toList());\n    }\n  }\n\n  /**\n   * An item indicating that an annotation appears on the i'th type in the throws clause of a method or constructor declaration.\n   */\n  class ThrowsTarget implements TargetInfo {\n    private final int throwsTypeIndex;\n\n    public ThrowsTarget(int throwsTypeIndex) {\n      this.throwsTypeIndex = throwsTypeIndex;\n    }\n\n    /**\n     * @return An index into the exception table array of the exceptions attribute of the method info structure. The index starts at 0.\n     */\n    public int getThrowsTypeIndex() {\n      return throwsTypeIndex;\n    }\n\n    /**\n     * @return All throws clause type annotations from a {@link TypeAnnotation} list at a specified throws type index.\n     */\n    public static List<TypeAnnotation> extract(List<TypeAnnotation> typeAnnotations, int throwsTypeIndex) {\n      return typeAnnotations.stream()\n        .filter(typeAnnotation -> {\n          TargetInfo targetInfo = typeAnnotation.getTargetInfo();\n          return targetInfo instanceof ThrowsTarget && ((ThrowsTarget)targetInfo).getThrowsTypeIndex() == throwsTypeIndex;\n        }).collect(Collectors.toList());\n    }\n  }\n\n  /**\n   * Item that indicates that an annotation appears on the declaration of the i'th type parameter of a generic class, generic interface,\n   * generic method, or generic constructor.\n   */\n  class TypeParameterTarget implements TargetInfo {\n    private final int typeParameterIndex;\n\n    public TypeParameterTarget(int typeParameterIndex) {\n      this.typeParameterIndex = typeParameterIndex;\n    }\n\n    /**\n     * @return an index indicating which parameter is annotated. The index starts at 0.\n     */\n    public int getTypeParameterIndex() {\n      return typeParameterIndex;\n    }\n\n    /**\n     * @return All type parameter type annotations from a {@link TypeAnnotation} list at a specified parameter index.\n     */\n    public static List<TypeAnnotation> extract(List<TypeAnnotation> typeAnnotations, int typeParameterIndex) {\n      return typeAnnotations.stream()\n        .filter(typeAnnotation -> {\n          TargetInfo targetInfo = typeAnnotation.getTargetInfo();\n          return targetInfo instanceof TypeParameterTarget\n                 && ((TypeParameterTarget)targetInfo).getTypeParameterIndex() == typeParameterIndex;\n        }).collect(Collectors.toList());\n    }\n  }\n\n  /**\n   * Item that indicates that an annotation appears on the i'th bound of the j'th type parameter declaration of a generic class, interface,\n   * method, or constructor.\n   */\n  class TypeParameterBoundTarget implements TargetInfo {\n    private final int typeParameterIndex;\n\n    private final int boundIndex;\n\n    public TypeParameterBoundTarget(int typeParameterIndex, int boundIndex) {\n      this.typeParameterIndex = typeParameterIndex;\n      this.boundIndex = boundIndex;\n    }\n\n    /**\n     * @return an index indicating which type parameter declaration has an annotated bound. The index starts at 0.\n     */\n    public int getTypeParameterIndex() {\n      return typeParameterIndex;\n    }\n\n    /**\n     * @return an index indicating which bound of the type parameter declaration indicated by getTypeParameterIndex is annotated.\n     */\n    public int getBoundIndex() {\n      return boundIndex;\n    }\n\n    /**\n     * @return All type parameter type annotations from a {@link TypeAnnotation} list at a specified parameter and bound index.\n     */\n    public static List<TypeAnnotation> extract(\n      List<TypeAnnotation> typeAnnotations,\n      int typeParameterIndex,\n      int boundIndex\n    ) {\n      return typeAnnotations.stream()\n        .filter(typeAnnotation -> {\n          TargetInfo targetInfo = typeAnnotation.getTargetInfo();\n          return targetInfo instanceof TypeParameterBoundTarget\n                 && ((TypeParameterBoundTarget)targetInfo).getTypeParameterIndex() == typeParameterIndex\n                 && ((TypeParameterBoundTarget)targetInfo).getBoundIndex() == boundIndex;\n        }).collect(Collectors.toList());\n    }\n  }\n\n  class TypeArgumentTarget implements TargetInfo {\n    private final int offset;\n\n    private final int typeArgumentIndex;\n\n    public TypeArgumentTarget(int offset, int typeArgumentIndex) {\n      this.offset = offset;\n      this.typeArgumentIndex = typeArgumentIndex;\n    }\n\n    public int getOffset() {\n      return offset;\n    }\n\n    public int getTypeArgumentIndex() {\n      return typeArgumentIndex;\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/typeann/TypeAnnotation.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.typeann;\n\nimport org.intellij.lang.annotations.MagicConstant;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent;\nimport org.jetbrains.java.decompiler.struct.StructMember;\nimport org.jetbrains.java.decompiler.struct.StructTypePathEntry;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructTypeAnnotationAttribute;\nimport org.jetbrains.java.decompiler.struct.gen.Type;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\npublic class TypeAnnotation {\n  public static final int CLASS_TYPE_PARAMETER = 0x00;\n  public static final int METHOD_TYPE_PARAMETER = 0x01;\n  public static final int SUPER_TYPE_REFERENCE = 0x10;\n  public static final int CLASS_TYPE_PARAMETER_BOUND = 0x11;\n  public static final int METHOD_TYPE_PARAMETER_BOUND = 0x12;\n  public static final int FIELD = 0x13;\n  public static final int METHOD_RETURN_TYPE = 0x14;\n  public static final int METHOD_RECEIVER = 0x15;\n  public static final int METHOD_PARAMETER = 0x16;\n  public static final int THROWS_REFERENCE = 0x17;\n  public static final int LOCAL_VARIABLE = 0x40;\n  public static final int RESOURCE_VARIABLE = 0x41;\n  public static final int CATCH_CLAUSE = 0x42;\n  public static final int EXPR_INSTANCEOF = 0x43;\n  public static final int EXPR_NEW = 0x44;\n  public static final int EXPR_CONSTRUCTOR_REF = 0x45;\n  public static final int EXPR_METHOD_REF = 0x46;\n  public static final int TYPE_ARG_CAST = 0x47;\n  public static final int TYPE_ARG_CONSTRUCTOR_CALL = 0x48;\n  public static final int TYPE_ARG_METHOD_CALL = 0x49;\n  public static final int TYPE_ARG_CONSTRUCTOR_REF = 0x4A;\n  public static final int TYPE_ARG_METHOD_REF = 0x4B;\n\n  private final int targetType;\n  private final TargetInfo targetInfo;\n  private final @NotNull List<StructTypePathEntry> paths;\n  private final @NotNull AnnotationExprent annotation;\n\n  public TypeAnnotation(\n    int targetType,\n    TargetInfo targetInfo,\n    @NotNull List<StructTypePathEntry> paths,\n    @NotNull AnnotationExprent annotation\n  ) {\n    this.targetType = targetType;\n    this.targetInfo = targetInfo;\n    this.paths = paths;\n    this.annotation = annotation;\n  }\n\n  @MagicConstant(valuesFromClass = TypeAnnotation.class)\n  public int getTargetType() {\n    return targetType;\n  }\n\n  public TargetInfo getTargetInfo() {\n    return targetInfo;\n  }\n\n  public @NotNull AnnotationExprent getAnnotationExpr() {\n    return annotation;\n  }\n\n  public @NotNull List<StructTypePathEntry> getPaths() {\n    return paths;\n  }\n\n  @Override\n  public String toString() {\n    return getAnnotationExpr().toJava(0, BytecodeMappingTracer.DUMMY).toString();\n  }\n\n  public void writeTo(@NotNull StringBuilder sb) {\n    sb.append(this);\n    sb.append(' ');\n  }\n\n  public void writeTo(@NotNull TextBuffer sb) {\n    sb.append(toString());\n    sb.append(' ');\n  }\n\n  public static List<TypeAnnotation> listFrom(StructMember md) {\n    return Arrays.stream(StructGeneralAttribute.TYPE_ANNOTATION_ATTRIBUTES)\n      .flatMap(attrKey -> {\n        StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute)md.getAttribute(attrKey);\n        if (attribute == null) {\n          return Stream.empty();\n        } else {\n          return attribute.getAnnotations().stream();\n        }\n      })\n      .collect(Collectors.toList());\n  }\n\n  public boolean isWrittenBeforeType(@NotNull Type type) {\n    StructTypePathEntry pathEntry = getPaths().stream().findFirst().orElse(null);\n    if (pathEntry == null && type.getArrayDim() == 0) {\n      return type.isAnnotatable();\n    }\n    if (pathEntry != null\n        && pathEntry.getTypePathEntryKind() == StructTypePathEntry.Kind.ARRAY.getId()\n        && getPaths().size() == type.getArrayDim()\n    ) return true;\n    return false;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/typeann/TypeAnnotationWriteHelper.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.typeann;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.struct.StructTypePathEntry;\nimport org.jetbrains.java.decompiler.util.TextBuffer;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n/**\n * Wrapper around {@link TypeAnnotation} to maintain the state of the {@link StructTypePathEntry} list while writing.\n */\npublic class TypeAnnotationWriteHelper {\n  private final @NotNull Deque<StructTypePathEntry> paths;\n\n  private final @NotNull TypeAnnotation annotation;\n\n  public TypeAnnotationWriteHelper(@NotNull TypeAnnotation annotation) {\n    this(annotation, new ArrayDeque<>(annotation.getPaths()));\n  }\n\n  public TypeAnnotationWriteHelper(@NotNull TypeAnnotation annotation, @NotNull Deque<StructTypePathEntry> paths) {\n    this.annotation = annotation;\n    this.paths = paths;\n  }\n\n  /**\n   * @return Active path relative to the current scope when writing.\n   */\n  public @NotNull Deque<StructTypePathEntry> getPaths() {\n    return paths;\n  }\n\n  /**\n   * @return The annotation to write\n   */\n  public @NotNull TypeAnnotation getAnnotation() {\n    return annotation;\n  }\n\n  public void writeTo(@NotNull StringBuilder sb) {\n    annotation.writeTo(sb);\n  }\n\n  public void writeTo(@NotNull TextBuffer sb) {\n    annotation.writeTo(sb);\n  }\n\n  public int arrayPathCount() {\n    return (int) paths.stream().filter(entry -> entry.getTypePathEntryKind() == StructTypePathEntry.Kind.ARRAY.getId()).count();\n  }\n\n  public static List<TypeAnnotationWriteHelper> create(List<TypeAnnotation> typeAnnotations) {\n    return typeAnnotations.stream()\n      .map(TypeAnnotationWriteHelper::new)\n      .collect(Collectors.toList());\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class CheckTypesResult {\n  private final List<ExprentTypePair> maxTypeExprents = new ArrayList<>();\n  private final List<ExprentTypePair> minTypeExprents = new ArrayList<>();\n\n  public void addMaxTypeExprent(Exprent exprent, VarType type) {\n    maxTypeExprents.add(new ExprentTypePair(exprent, type));\n  }\n\n  public void addMinTypeExprent(Exprent exprent, VarType type) {\n    minTypeExprents.add(new ExprentTypePair(exprent, type));\n  }\n\n  public List<ExprentTypePair> getMaxTypeExprents() {\n    return maxTypeExprents;\n  }\n\n  public List<ExprentTypePair> getMinTypeExprents() {\n    return minTypeExprents;\n  }\n\n  static class ExprentTypePair {\n    public final Exprent exprent;\n    public final VarType type;\n\n    ExprentTypePair(Exprent exprent, VarType type) {\n      this.exprent = exprent;\n      this.type = type;\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement.LoopType;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class VarDefinitionHelper {\n\n  private final HashMap<Integer, Statement> mapVarDefStatements;\n\n  // statement.id, defined vars\n  private final HashMap<Integer, HashSet<Integer>> mapStatementVars;\n\n  private final HashSet<Integer> implDefVars;\n\n  private final VarProcessor varproc;\n\n  public VarDefinitionHelper(Statement root, StructMethod mt, VarProcessor varproc) {\n\n    mapVarDefStatements = new HashMap<>();\n    mapStatementVars = new HashMap<>();\n    implDefVars = new HashSet<>();\n\n    this.varproc = varproc;\n\n    VarNamesCollector vc = varproc.getVarNamesCollector();\n\n    boolean thisvar = !mt.hasModifier(CodeConstants.ACC_STATIC);\n\n    MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n\n    int paramcount = 0;\n    if (thisvar) {\n      paramcount = 1;\n    }\n    paramcount += md.params.length;\n\n\n    // method parameters are implicitly defined\n    int varindex = 0;\n    for (int i = 0; i < paramcount; i++) {\n      implDefVars.add(varindex);\n      varproc.setVarName(new VarVersionPair(varindex, 0), vc.getFreeName(varindex));\n\n      if (thisvar) {\n        if (i == 0) {\n          varindex++;\n        }\n        else {\n          varindex += md.params[i - 1].getStackSize();\n        }\n      }\n      else {\n        varindex += md.params[i].getStackSize();\n      }\n    }\n\n    if (thisvar) {\n      StructClass current_class = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS);\n\n      varproc.getThisVars().put(new VarVersionPair(0, 0), current_class.qualifiedName);\n      varproc.setVarName(new VarVersionPair(0, 0), \"this\");\n      vc.addName(\"this\");\n    }\n\n    // catch variables are implicitly defined\n    LinkedList<Statement> stack = new LinkedList<>();\n    stack.add(root);\n\n    while (!stack.isEmpty()) {\n      Statement st = stack.removeFirst();\n\n      List<VarExprent> lstVars = null;\n      if (st.type == StatementType.CATCH_ALL) {\n        lstVars = ((CatchAllStatement)st).getVars();\n      }\n      else if (st.type == StatementType.TRY_CATCH) {\n        lstVars = ((CatchStatement)st).getVars();\n      }\n\n      if (lstVars != null) {\n        for (VarExprent var : lstVars) {\n          implDefVars.add(var.getIndex());\n          varproc.setVarName(new VarVersionPair(var), vc.getFreeName(var.getIndex()));\n          var.setDefinition(true);\n        }\n      }\n\n      stack.addAll(st.getStats());\n    }\n\n    initStatement(root);\n  }\n\n\n  public void setVarDefinitions() {\n    VarNamesCollector vc = varproc.getVarNamesCollector();\n\n    for (Entry<Integer, Statement> en : mapVarDefStatements.entrySet()) {\n      Statement stat = en.getValue();\n      Integer index = en.getKey();\n\n      if (implDefVars.contains(index)) {\n        // already implicitly defined\n        continue;\n      }\n\n      varproc.setVarName(new VarVersionPair(index.intValue(), 0), vc.getFreeName(index));\n\n      // special case for\n      if (stat.type == StatementType.DO) {\n        DoStatement dstat = (DoStatement)stat;\n        if (dstat.getLoopType() == LoopType.FOR) {\n\n          if (dstat.getInitExprent() != null && setDefinition(dstat.getInitExprent(), index)) {\n            continue;\n          }\n          else {\n            List<Exprent> lstSpecial = Arrays.asList(dstat.getConditionExprent(), dstat.getIncExprent());\n            for (VarExprent var : getAllVars(lstSpecial)) {\n              if (var.getIndex() == index) {\n                stat = stat.getParent();\n                break;\n              }\n            }\n          }\n        }\n      }\n\n\n      Statement first = findFirstBlock(stat, index);\n\n      List<Exprent> lst;\n      if (first == null) {\n        lst = stat.getVarDefinitions();\n      }\n      else if (first.getExprents() == null) {\n        lst = first.getVarDefinitions();\n      }\n      else {\n        lst = first.getExprents();\n      }\n\n\n      boolean defset = false;\n\n      // search for the first assignment to var [index]\n      int addindex = 0;\n      for (Exprent expr : lst) {\n        if (setDefinition(expr, index)) {\n          defset = true;\n          break;\n        }\n        else {\n          boolean foundvar = false;\n          for (Exprent exp : expr.getAllExprents(true)) {\n            if (exp.type == Exprent.EXPRENT_VAR && ((VarExprent)exp).getIndex() == index) {\n              foundvar = true;\n              break;\n            }\n          }\n          if (foundvar) {\n            break;\n          }\n        }\n        addindex++;\n      }\n\n      if (!defset) {\n        VarExprent var = new VarExprent(index, varproc.getVarType(new VarVersionPair(index.intValue(), 0)), varproc);\n        var.setDefinition(true);\n\n        lst.add(addindex, var);\n      }\n    }\n  }\n\n\n  // *****************************************************************************\n  // private methods\n  // *****************************************************************************\n\n  private Statement findFirstBlock(Statement stat, Integer varindex) {\n\n    LinkedList<Statement> stack = new LinkedList<>();\n    stack.add(stat);\n\n    while (!stack.isEmpty()) {\n      Statement st = stack.remove(0);\n\n      if (stack.isEmpty() || mapStatementVars.get(st.id).contains(varindex)) {\n\n        if (st.isLabeled() && !stack.isEmpty()) {\n          return st;\n        }\n\n        if (st.getExprents() != null) {\n          return st;\n        }\n        else {\n          stack.clear();\n\n          switch (st.type) {\n            case SEQUENCE -> stack.addAll(0, st.getStats());\n            case IF, ROOT, SWITCH, SYNCHRONIZED -> stack.add(st.getFirst());\n            default -> {\n              return st;\n            }\n          }\n        }\n      }\n    }\n\n    return null;\n  }\n\n  private Set<Integer> initStatement(Statement stat) {\n\n    HashMap<Integer, Integer> mapCount = new HashMap<>();\n\n    List<VarExprent> condlst;\n\n    if (stat.getExprents() == null) {\n\n      // recurse on children statements\n      List<Integer> childVars = new ArrayList<>();\n      List<Exprent> currVars = new ArrayList<>();\n\n      for (Object obj : stat.getSequentialObjects()) {\n        if (obj instanceof Statement st) {\n          childVars.addAll(initStatement(st));\n\n          if (st.type == StatementType.DO) {\n            DoStatement dost = (DoStatement)st;\n            if (dost.getLoopType() != LoopType.FOR &&\n                dost.getLoopType() != LoopType.DO) {\n              currVars.add(dost.getConditionExprent());\n            }\n          }\n          else if (st.type == StatementType.CATCH_ALL) {\n            CatchAllStatement fin = (CatchAllStatement)st;\n            if (fin.isFinally() && fin.getMonitor() != null) {\n              currVars.add(fin.getMonitor());\n            }\n          }\n        }\n        else if (obj instanceof Exprent) {\n          currVars.add((Exprent)obj);\n        }\n      }\n\n      // children statements\n      for (Integer index : childVars) {\n        Integer count = mapCount.get(index);\n        if (count == null) {\n          count = 0;\n        }\n        mapCount.put(index, count + 1);\n      }\n\n      condlst = getAllVars(currVars);\n    }\n    else {\n      condlst = getAllVars(stat.getExprents());\n    }\n\n    // this statement\n    for (VarExprent var : condlst) {\n      mapCount.put(var.getIndex(), 2);\n    }\n\n\n    HashSet<Integer> set = new HashSet<>(mapCount.keySet());\n\n    // put all variables defined in this statement into the set\n    for (Entry<Integer, Integer> en : mapCount.entrySet()) {\n      if (en.getValue() > 1) {\n        mapVarDefStatements.put(en.getKey(), stat);\n      }\n    }\n\n    mapStatementVars.put(stat.id, set);\n\n    return set;\n  }\n\n  private static List<VarExprent> getAllVars(List<Exprent> lst) {\n\n    List<VarExprent> res = new ArrayList<>();\n    List<Exprent> listTemp = new ArrayList<>();\n\n    for (Exprent expr : lst) {\n      listTemp.addAll(expr.getAllExprents(true));\n      listTemp.add(expr);\n    }\n\n    for (Exprent exprent : listTemp) {\n      if (exprent.type == Exprent.EXPRENT_VAR) {\n        res.add((VarExprent)exprent);\n      }\n    }\n\n    return res;\n  }\n\n  private static boolean setDefinition(Exprent expr, Integer index) {\n    if (expr.type == Exprent.EXPRENT_ASSIGNMENT) {\n      Exprent left = ((AssignmentExprent)expr).getLeft();\n      if (left.type == Exprent.EXPRENT_VAR) {\n        VarExprent var = (VarExprent)left;\n        if (var.getIndex() == index) {\n          var.setDefinition(true);\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.TextUtil;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class VarProcessor {\n  public static final int VAR_NON_FINAL = 1;\n  public static final int VAR_EXPLICIT_FINAL = 2;\n  public static final int VAR_FINAL = 3;\n\n  public StructMethod getMethod() {\n    return method;\n  }\n\n  private final VarNamesCollector varNamesCollector = new VarNamesCollector();\n  private final StructMethod method;\n  private final MethodDescriptor methodDescriptor;\n  private Map<VarVersionPair, String> mapVarNames = new HashMap<>();\n  private VarVersionsProcessor varVersions;\n  private final Map<VarVersionPair, String> thisVars = new HashMap<>();\n  private final Set<VarVersionPair> externalVars = new HashSet<>();\n  private final BitSet finalParameters = new BitSet();\n  private final int firstParameterVarIndex;\n  private final int firstParameterPosition;\n\n  public VarProcessor(StructClass cl, StructMethod mt, MethodDescriptor md) {\n    method = mt;\n    methodDescriptor = md;\n    boolean isEnum = cl.hasModifier(CodeConstants.ACC_ENUM) && DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM);\n    boolean isEnumInit = isEnum && CodeConstants.INIT_NAME.equals(mt.getName());\n    firstParameterVarIndex = isEnumInit ? 3 : !mt.hasModifier(CodeConstants.ACC_STATIC) ? 1 : 0;\n    firstParameterPosition = isEnumInit ? 2 : 0;\n  }\n\n  public void setVarVersions(RootStatement root) {\n    VarVersionsProcessor oldProcessor = varVersions;\n    varVersions = new VarVersionsProcessor(method, methodDescriptor);\n    varVersions.setVarVersions(root, oldProcessor);\n  }\n\n  public void setVarDefinitions(Statement root) {\n    mapVarNames = new HashMap<>();\n    new VarDefinitionHelper(root, method, this).setVarDefinitions();\n  }\n\n  public void setDebugVarNames(Map<Integer, String> mapDebugVarNames) {\n    if (varVersions == null) {\n      return;\n    }\n\n    Map<Integer, Integer> mapOriginalVarIndices = varVersions.getMapOriginalVarIndices();\n\n    List<VarVersionPair> listVars = new ArrayList<>(mapVarNames.keySet());\n    listVars.sort(Comparator.comparingInt(o -> o.var));\n\n    Map<String, Integer> mapNames = new HashMap<>();\n\n    for (VarVersionPair pair : listVars) {\n      String name = mapVarNames.get(pair);\n\n      Integer index = mapOriginalVarIndices.get(pair.var);\n      if (index != null) {\n        String debugName = mapDebugVarNames.get(index);\n        if (debugName != null && TextUtil.isValidIdentifier(debugName, method.getBytecodeVersion())) {\n          name = debugName;\n        }\n      }\n\n      Integer counter = mapNames.get(name);\n      mapNames.put(name, counter == null ? counter = 0 : ++counter);\n\n      if (counter > 0) {\n        name += String.valueOf(counter);\n      }\n\n      mapVarNames.put(pair, name);\n    }\n  }\n\n  public Integer getVarOriginalIndex(int index) {\n    return varVersions == null ? null : varVersions.getMapOriginalVarIndices().get(index);\n  }\n\n  public void refreshVarNames(VarNamesCollector vc) {\n    Map<VarVersionPair, String> tempVarNames = new HashMap<>(mapVarNames);\n    for (Entry<VarVersionPair, String> ent : tempVarNames.entrySet()) {\n      mapVarNames.put(ent.getKey(), vc.getFreeName(ent.getValue()));\n    }\n  }\n\n  public VarNamesCollector getVarNamesCollector() {\n    return varNamesCollector;\n  }\n\n  public VarType getVarType(VarVersionPair pair) {\n    return varVersions == null ? null : varVersions.getVarType(pair);\n  }\n\n  public void setVarType(VarVersionPair pair, VarType type) {\n    varVersions.setVarType(pair, type);\n  }\n\n  public String getVarName(VarVersionPair pair) {\n    return mapVarNames == null ? null : mapVarNames.get(pair);\n  }\n\n  public void setVarName(VarVersionPair pair, String name) {\n    mapVarNames.put(pair, name);\n  }\n\n  public Collection<String> getVarNames() {\n    return mapVarNames != null ? mapVarNames.values() : Collections.emptySet();\n  }\n\n  public int getVarFinal(VarVersionPair pair) {\n    return varVersions == null ? VAR_FINAL : varVersions.getVarFinal(pair);\n  }\n\n  public void setVarFinal(VarVersionPair pair, int finalType) {\n    varVersions.setVarFinal(pair, finalType);\n  }\n\n  public Map<VarVersionPair, String> getThisVars() {\n    return thisVars;\n  }\n\n  public Set<VarVersionPair> getExternalVars() {\n    return externalVars;\n  }\n\n  public boolean isParameterFinal(VarVersionPair pair) {\n    return finalParameters.get(pair.var);\n  }\n\n  public void setParameterFinal(VarVersionPair pair) {\n    finalParameters.set(pair.var);\n  }\n\n  public int getFirstParameterVarIndex() {\n    return firstParameterVarIndex;\n  }\n\n  public int getFirstParameterPosition() {\n    return firstParameterPosition;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.CatchAllStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\n\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\npublic class VarTypeProcessor {\n  private final StructMethod method;\n  private final MethodDescriptor methodDescriptor;\n\n  private final Map<VarVersionPair, VarType> minExprentTypes = new HashMap<>();\n  private final Map<VarVersionPair, VarType> maxExprentTypes = new HashMap<>();\n  private final Map<VarVersionPair, Integer> finalVariables = new HashMap<>();\n\n  public VarTypeProcessor(@NotNull StructMethod method, @NotNull MethodDescriptor methodDescriptor) {\n    this.method = method;\n    this.methodDescriptor = methodDescriptor;\n  }\n\n  public void calculateVarTypes(@NotNull RootStatement root, @NotNull DirectGraph graph) {\n    setInitVariables();\n    setCatchBlockVariables(root);\n    resetExprentTypes(graph);\n    //noinspection StatementWithEmptyBody\n    while (!processVarTypes(graph)) ;\n  }\n\n  private void setInitVariables() {\n    boolean thisVar = !method.hasModifier(CodeConstants.ACC_STATIC);\n    if (thisVar) {\n      StructClass currentClass = (StructClass)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS);\n      VarType classType = new VarType(CodeConstants.TYPE_OBJECT, 0, currentClass.qualifiedName);\n      minExprentTypes.put(new VarVersionPair(0, 1), classType);\n      maxExprentTypes.put(new VarVersionPair(0, 1), classType);\n    }\n    int varIndex = 0;\n    VarType[] methodParameters = methodDescriptor.params;\n    for (VarType parameter : methodParameters) {\n      minExprentTypes.put(new VarVersionPair(varIndex + (thisVar ? 1 : 0), 1), parameter);\n      maxExprentTypes.put(new VarVersionPair(varIndex + (thisVar ? 1 : 0), 1), parameter);\n      varIndex += parameter.getStackSize();\n    }\n  }\n\n  private void setCatchBlockVariables(@NotNull RootStatement root) {\n    LinkedList<Statement> statements = new LinkedList<>();\n    statements.add(root);\n    while (!statements.isEmpty()) {\n      Statement statement = statements.removeFirst();\n      List<VarExprent> catchVariables = null;\n      if (statement.type == StatementType.CATCH_ALL) {\n        catchVariables = ((CatchAllStatement)statement).getVars();\n      }\n      else if (statement.type == StatementType.TRY_CATCH) {\n        catchVariables = ((CatchStatement)statement).getVars();\n      }\n      if (catchVariables != null) {\n        for (VarExprent catchVariable : catchVariables) {\n          minExprentTypes.put(new VarVersionPair(catchVariable.getIndex(), 1), catchVariable.getVarType());\n          maxExprentTypes.put(new VarVersionPair(catchVariable.getIndex(), 1), catchVariable.getVarType());\n        }\n      }\n      statements.addAll(statement.getStats());\n    }\n  }\n\n  private boolean checkTypeExprent(@NotNull Exprent currentExprent) {\n    for (Exprent exprent : currentExprent.getAllExprents()) {\n      if (!checkTypeExprent(exprent)) return false;\n    }\n    if (currentExprent.type == Exprent.EXPRENT_CONST) {\n      ConstExprent constExprent = (ConstExprent)currentExprent;\n      if (constExprent.getConstType().getTypeFamily() <= CodeConstants.TYPE_FAMILY_INTEGER) { // boolean or integer\n        VarVersionPair varVersion = new VarVersionPair(constExprent.id, -1);\n        if (!minExprentTypes.containsKey(varVersion)) {\n          minExprentTypes.put(varVersion, constExprent.getConstType());\n        }\n      }\n    }\n\n    CheckTypesResult exprentTypeBounds = currentExprent.checkExprTypeBounds();\n    if (exprentTypeBounds == null) return true;\n\n    for (var entry : exprentTypeBounds.getMaxTypeExprents()) {\n      if (entry.type.getTypeFamily() != CodeConstants.TYPE_FAMILY_OBJECT) {\n        changeExprentType(entry.exprent, entry.type, false);\n      }\n    }\n    boolean result = true;\n    for (var entry : exprentTypeBounds.getMinTypeExprents()) {\n      result &= changeExprentType(entry.exprent, entry.type, true);\n    }\n    return result;\n  }\n\n  private boolean processVarTypes(@NotNull DirectGraph graph) {\n    return graph.iterateExprents(exprent -> checkTypeExprent(exprent) ? 0 : 1);\n  }\n\n  private boolean changeExprentType(@NotNull Exprent exprent, @NotNull VarType newType, boolean checkMinExprentType) {\n    if (exprent.type == Exprent.EXPRENT_CONST) {\n      ConstExprent constExprent = (ConstExprent)exprent;\n      VarType constType = constExprent.getConstType();\n      if (newType.getTypeFamily() > CodeConstants.TYPE_FAMILY_INTEGER || constType.getTypeFamily() > CodeConstants.TYPE_FAMILY_INTEGER) return true;\n      if (newType.getTypeFamily() == CodeConstants.TYPE_FAMILY_INTEGER) {\n        VarType integerType = new ConstExprent((Integer)constExprent.getValue(), false, null).getConstType();\n        if (integerType.isStrictSuperset(newType)) {\n          newType = integerType;\n        }\n      }\n      return changeConstExprentType(new VarVersionPair(exprent.id, -1), exprent, newType, checkMinExprentType);\n    }\n    if (exprent.type == Exprent.EXPRENT_VAR) {\n      return changeConstExprentType(new VarVersionPair((VarExprent)exprent), exprent, newType, checkMinExprentType);\n    }\n    if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {\n      return changeExprentType(((AssignmentExprent)exprent).getRight(), newType, checkMinExprentType);\n    }\n    if (exprent.type == Exprent.EXPRENT_FUNCTION) {\n      FunctionExprent functionExprent = (FunctionExprent)exprent;\n      switch (functionExprent.getFuncType()) {\n        case FunctionExprent.FUNCTION_IIF -> {   // FIXME:\n          return changeExprentType(functionExprent.getLstOperands().get(1), newType, checkMinExprentType) &\n                 changeExprentType(functionExprent.getLstOperands().get(2), newType, checkMinExprentType);\n        }\n        case FunctionExprent.FUNCTION_AND, FunctionExprent.FUNCTION_OR, FunctionExprent.FUNCTION_XOR -> {\n          return changeExprentType(functionExprent.getLstOperands().get(0), newType, checkMinExprentType) &\n                 changeExprentType(functionExprent.getLstOperands().get(1), newType, checkMinExprentType);\n        }\n      }\n    }\n    return true;\n  }\n\n  private boolean changeConstExprentType(@NotNull VarVersionPair varVersion,\n                                         @NotNull Exprent exprent,\n                                         @NotNull VarType newType,\n                                         boolean checkMinExprentType) {\n    if (checkMinExprentType) {\n      VarType currentMinType = minExprentTypes.get(varVersion);\n      VarType newMinType;\n      if (currentMinType == null || newType.getTypeFamily() > currentMinType.getTypeFamily()) {\n        newMinType = newType;\n      }\n      else if (newType.getTypeFamily() < currentMinType.getTypeFamily()) {\n        return true;\n      }\n      else {\n        newMinType = VarType.getCommonSupertype(currentMinType, newType);\n      }\n      minExprentTypes.put(varVersion, newMinType);\n      if (exprent.type == Exprent.EXPRENT_CONST) {\n        ((ConstExprent)exprent).setConstType(newMinType);\n      }\n      return currentMinType == null ||\n             (newMinType.getTypeFamily() <= currentMinType.getTypeFamily() && !newMinType.isStrictSuperset(currentMinType));\n    }\n    VarType currentMaxType = maxExprentTypes.get(varVersion);\n    VarType newMaxType;\n    if (currentMaxType == null || newType.getTypeFamily() < currentMaxType.getTypeFamily()) {\n      newMaxType = newType;\n    }\n    else if (newType.getTypeFamily() > currentMaxType.getTypeFamily()) {\n      return true;\n    }\n    else {\n      newMaxType = VarType.getCommonMinType(currentMaxType, newType);\n    }\n    maxExprentTypes.put(varVersion, newMaxType);\n    return true;\n  }\n\n  private static void resetExprentTypes(@NotNull DirectGraph graph) {\n    graph.iterateExprents(currExprent -> {\n      List<Exprent> allExprents = currExprent.getAllExprents(true);\n      allExprents.add(currExprent);\n      for (Exprent exprent : allExprents) {\n        if (exprent.type == Exprent.EXPRENT_VAR) {\n          ((VarExprent)exprent).setVarType(VarType.VARTYPE_UNKNOWN);\n        }\n        else if (exprent.type == Exprent.EXPRENT_CONST) {\n          ConstExprent constExprent = (ConstExprent)exprent;\n          if (constExprent.getConstType().getTypeFamily() == CodeConstants.TYPE_FAMILY_INTEGER) {\n            constExprent.setConstType(new ConstExprent(constExprent.getIntValue(), constExprent.isBoolPermitted(), null).getConstType());\n          }\n        }\n      }\n      return 0;\n    });\n  }\n\n  public Map<VarVersionPair, VarType> getMaxExprentTypes() {\n    return maxExprentTypes;\n  }\n\n  public Map<VarVersionPair, VarType> getMinExprentTypes() {\n    return minExprentTypes;\n  }\n\n  public Map<VarVersionPair, Integer> getFinalVariables() {\n    return finalVariables;\n  }\n\n  public VarType getVarType(VarVersionPair pair) {\n    return minExprentTypes.get(pair);\n  }\n\n  public void setVarType(VarVersionPair pair, VarType type) {\n    minExprentTypes.put(pair, type);\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\npublic class VarVersionEdge { // FIXME: can be removed?\n\n  public static final int EDGE_GENERAL = 0;\n  public static final int EDGE_PHANTOM = 1;\n\n  public final int type;\n\n  public final VarVersionNode source;\n\n  public final VarVersionNode dest;\n\n  private final int hashCode;\n\n  public VarVersionEdge(int type, VarVersionNode source, VarVersionNode dest) {\n    this.type = type;\n    this.source = source;\n    this.dest = dest;\n    this.hashCode = source.hashCode() ^ dest.hashCode() + type;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof VarVersionEdge edge)) return false;\n\n    return type == edge.type && source == edge.source && dest == edge.dest;\n  }\n\n  @Override\n  public int hashCode() {\n    return hashCode;\n  }\n\n  @Override\n  public String toString() {\n    return source.toString() + \" ->\" + type + \"-> \" + dest.toString();\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;\nimport org.jetbrains.java.decompiler.util.SFormsFastMapDirect;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class VarVersionNode implements IGraphNode {\n  public static final int FLAG_PHANTOM_FIN_EXIT = 2;\n\n  public final int var;\n  public final int version;\n  public final Set<VarVersionEdge> predecessors = new HashSet<>();\n  public final Set<VarVersionEdge> successors = new HashSet<>();\n\n  public int flags;\n  public SFormsFastMapDirect live = new SFormsFastMapDirect();\n\n  public VarVersionNode(int var, int version) {\n    this.var = var;\n    this.version = version;\n  }\n\n  public void addPredecessor(VarVersionEdge edge) {\n    predecessors.add(edge);\n  }\n\n  public void removePredecessor(VarVersionEdge edge) {\n    predecessors.remove(edge);\n  }\n\n  public void addSuccessor(VarVersionEdge edge) {\n    successors.add(edge);\n  }\n\n  public void removeSuccessor(VarVersionEdge edge) {\n    successors.remove(edge);\n  }\n\n  @Override\n  public List<IGraphNode> getPredecessorNodes() {\n    List<IGraphNode> lst = new ArrayList<>(predecessors.size());\n    for (VarVersionEdge edge : predecessors) {\n      lst.add(edge.source);\n    }\n    return lst;\n  }\n\n  @Override\n  public String toString() {\n    return \"(\" + var + '_' + version + ')';\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPair.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\n\npublic class VarVersionPair {\n\n  public final int var;\n  public final int version;\n\n  private int hashCode = -1;\n\n  public VarVersionPair(int var, int version) {\n    this.var = var;\n    this.version = version;\n  }\n\n  public VarVersionPair(Integer var, Integer version) {\n    this.var = var;\n    this.version = version;\n  }\n\n  public VarVersionPair(VarExprent var) {\n    this.var = var.getIndex();\n    this.version = var.getVersion();\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof VarVersionPair paar)) return false;\n\n    return var == paar.var && version == paar.version;\n  }\n\n  @Override\n  public int hashCode() {\n    if (hashCode == -1) {\n      hashCode = this.var * 3 + this.version;\n    }\n    return hashCode;\n  }\n\n  @Override\n  public String toString() {\n    return \"(\" + var + \",\" + version + \")\";\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine;\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.util.*;\n\npublic class VarVersionsGraph {\n  public final VBStyleCollection<VarVersionNode, VarVersionPair> nodes = new VBStyleCollection<>();\n\n  private GenericDominatorEngine engine;\n\n  public VarVersionNode createNode(VarVersionPair ver) {\n    VarVersionNode node;\n    nodes.addWithKey(node = new VarVersionNode(ver.var, ver.version), ver);\n    return node;\n  }\n\n  public void addNodes(Collection<VarVersionNode> colnodes, Collection<VarVersionPair> colpaars) {\n    nodes.addAllWithKey(colnodes, colpaars);\n  }\n\n  public boolean isDominatorSet(VarVersionNode node, Set<VarVersionNode> domnodes) {\n    if (domnodes.size() == 1) {\n      return engine.isDominator(node, domnodes.iterator().next());\n    }\n    else {\n      Set<VarVersionNode> marked = new HashSet<>();\n\n      if (domnodes.contains(node)) {\n        return true;\n      }\n\n      List<VarVersionNode> lstNodes = new LinkedList<>();\n      lstNodes.add(node);\n\n      while (!lstNodes.isEmpty()) {\n        VarVersionNode nd = lstNodes.remove(0);\n        if (marked.contains(nd)) {\n          continue;\n        }\n        else {\n          marked.add(nd);\n        }\n\n        if (nd.predecessors.isEmpty()) {\n          return false;\n        }\n\n        for (VarVersionEdge edge : nd.predecessors) {\n          VarVersionNode pred = edge.source;\n          if (!marked.contains(pred) && !domnodes.contains(pred)) {\n            lstNodes.add(pred);\n          }\n        }\n      }\n    }\n\n    return true;\n  }\n\n  public void initDominators() {\n    Set<VarVersionNode> roots = new HashSet<>();\n\n    for (VarVersionNode node : nodes) {\n      if (node.predecessors.isEmpty()) {\n        roots.add(node);\n      }\n    }\n\n    engine = new GenericDominatorEngine(new IGraph() {\n      @Override\n      public List<? extends IGraphNode> getReversePostOrderList() {\n        return getReversedPostOrder(roots);\n      }\n\n      @Override\n      public Set<? extends IGraphNode> getRoots() {\n        return new HashSet<IGraphNode>(roots);\n      }\n    });\n\n    engine.initialize();\n  }\n\n  private static List<VarVersionNode> getReversedPostOrder(Collection<VarVersionNode> roots) {\n    List<VarVersionNode> lst = new LinkedList<>();\n    Set<VarVersionNode> setVisited = new HashSet<>();\n\n    for (VarVersionNode root : roots) {\n      List<VarVersionNode> lstTemp = new LinkedList<>();\n      addToReversePostOrderListIterative(root, lstTemp, setVisited);\n      lst.addAll(lstTemp);\n    }\n\n    return lst;\n  }\n\n  private static void addToReversePostOrderListIterative(VarVersionNode root, List<? super VarVersionNode> lst, Set<? super VarVersionNode> setVisited) {\n    Map<VarVersionNode, List<VarVersionEdge>> mapNodeSuccs = new HashMap<>();\n    LinkedList<VarVersionNode> stackNode = new LinkedList<>();\n    LinkedList<Integer> stackIndex = new LinkedList<>();\n\n    stackNode.add(root);\n    stackIndex.add(0);\n\n    while (!stackNode.isEmpty()) {\n      VarVersionNode node = stackNode.getLast();\n      int index = stackIndex.removeLast();\n\n      setVisited.add(node);\n\n      List<VarVersionEdge> lstSuccs = mapNodeSuccs.computeIfAbsent(node, n -> new ArrayList<>(n.successors));\n      for (; index < lstSuccs.size(); index++) {\n        VarVersionNode succ = lstSuccs.get(index).dest;\n\n        if (!setVisited.contains(succ)) {\n          stackIndex.add(index + 1);\n          stackNode.add(succ);\n          stackIndex.add(0);\n          break;\n        }\n      }\n\n      if (index == lstSuccs.size()) {\n        lst.add(0, node);\n        stackNode.removeLast();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.decompiler.vars;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.collectors.CounterContainer;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper;\nimport org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;\n\nimport java.util.*;\nimport java.util.Map.Entry;\n\npublic class VarVersionsProcessor {\n  private final StructMethod method;\n  private Map<Integer, Integer> mapOriginalVarIndices = Collections.emptyMap();\n  private final VarTypeProcessor typeProcessor;\n\n  public VarVersionsProcessor(StructMethod mt, MethodDescriptor md) {\n    method = mt;\n    typeProcessor = new VarTypeProcessor(mt, md);\n  }\n\n  public void setVarVersions(RootStatement root, VarVersionsProcessor previousVersionsProcessor) {\n    SSAConstructorSparseEx ssa = new SSAConstructorSparseEx();\n    ssa.splitVariables(root, method);\n\n    FlattenStatementsHelper flattenHelper = new FlattenStatementsHelper();\n    DirectGraph graph = flattenHelper.buildDirectGraph(root);\n\n    mergePhiVersions(ssa, graph);\n\n    typeProcessor.calculateVarTypes(root, graph);\n\n    simpleMerge(typeProcessor, graph, method);\n\n    // FIXME: advanced merging\n\n    eliminateNonJavaTypes(typeProcessor);\n\n    setNewVarIndices(typeProcessor, graph, previousVersionsProcessor);\n  }\n\n  private static void mergePhiVersions(SSAConstructorSparseEx ssa, DirectGraph graph) {\n    // collect phi versions\n    List<Set<VarVersionPair>> lst = new ArrayList<>();\n    for (Entry<VarVersionPair, FastSparseSet<Integer>> ent : ssa.getPhi().entrySet()) {\n      Set<VarVersionPair> set = new HashSet<>();\n      set.add(ent.getKey());\n      for (Integer version : ent.getValue()) {\n        set.add(new VarVersionPair(ent.getKey().var, version.intValue()));\n      }\n\n      for (int i = lst.size() - 1; i >= 0; i--) {\n        Set<VarVersionPair> tset = lst.get(i);\n        Set<VarVersionPair> intersection = new HashSet<>(set);\n        intersection.retainAll(tset);\n\n        if (!intersection.isEmpty()) {\n          set.addAll(tset);\n          lst.remove(i);\n        }\n      }\n\n      lst.add(set);\n    }\n\n    Map<VarVersionPair, Integer> phiVersions = new HashMap<>();\n    for (Set<VarVersionPair> set : lst) {\n      int min = Integer.MAX_VALUE;\n      for (VarVersionPair paar : set) {\n        if (paar.version < min) {\n          min = paar.version;\n        }\n      }\n\n      for (VarVersionPair paar : set) {\n        phiVersions.put(new VarVersionPair(paar.var, paar.version), min);\n      }\n    }\n\n    updateVersions(graph, phiVersions);\n  }\n\n  private static void updateVersions(DirectGraph graph, final Map<VarVersionPair, Integer> versions) {\n    graph.iterateExprents(exprent -> {\n      List<Exprent> lst = exprent.getAllExprents(true);\n      lst.add(exprent);\n\n      for (Exprent expr : lst) {\n        if (expr.type == Exprent.EXPRENT_VAR) {\n          VarExprent var = (VarExprent)expr;\n          Integer version = versions.get(new VarVersionPair(var));\n          if (version != null) {\n            var.setVersion(version);\n          }\n        }\n      }\n\n      return 0;\n    });\n  }\n\n  private static void eliminateNonJavaTypes(VarTypeProcessor typeProcessor) {\n    Map<VarVersionPair, VarType> mapExprentMaxTypes = typeProcessor.getMaxExprentTypes();\n    Map<VarVersionPair, VarType> mapExprentMinTypes = typeProcessor.getMinExprentTypes();\n\n    for (VarVersionPair paar : new ArrayList<>(mapExprentMinTypes.keySet())) {\n      VarType type = mapExprentMinTypes.get(paar);\n      VarType maxType = mapExprentMaxTypes.get(paar);\n\n      if (type.getType() == CodeConstants.TYPE_BYTECHAR || type.getType() == CodeConstants.TYPE_SHORTCHAR) {\n        if (maxType != null && maxType.getType() == CodeConstants.TYPE_CHAR) {\n          type = VarType.VARTYPE_CHAR;\n        }\n        else {\n          type = type.getType() == CodeConstants.TYPE_BYTECHAR ? VarType.VARTYPE_BYTE : VarType.VARTYPE_SHORT;\n        }\n        mapExprentMinTypes.put(paar, type);\n        //} else if(type.type == CodeConstants.TYPE_CHAR && (maxType == null || maxType.type == CodeConstants.TYPE_INT)) { // when possible, lift char to int\n        //\tmapExprentMinTypes.put(paar, VarType.VARTYPE_INT);\n      }\n      else if (type.getType() == CodeConstants.TYPE_NULL) {\n        mapExprentMinTypes.put(paar, VarType.VARTYPE_OBJECT);\n      }\n    }\n  }\n\n  private static void simpleMerge(VarTypeProcessor typeProcessor, DirectGraph graph, StructMethod mt) {\n    Map<VarVersionPair, VarType> mapExprentMaxTypes = typeProcessor.getMaxExprentTypes();\n    Map<VarVersionPair, VarType> mapExprentMinTypes = typeProcessor.getMinExprentTypes();\n\n    Map<Integer, Set<Integer>> mapVarVersions = new HashMap<>();\n\n    for (VarVersionPair pair : mapExprentMinTypes.keySet()) {\n      if (pair.version >= 0) {  // don't merge constants\n        mapVarVersions.computeIfAbsent(pair.var, k -> new HashSet<>()).add(pair.version);\n      }\n    }\n\n    boolean is_method_static = mt.hasModifier(CodeConstants.ACC_STATIC);\n\n    Map<VarVersionPair, Integer> mapMergedVersions = new HashMap<>();\n\n    for (Entry<Integer, Set<Integer>> ent : mapVarVersions.entrySet()) {\n\n      if (ent.getValue().size() > 1) {\n        List<Integer> lstVersions = new ArrayList<>(ent.getValue());\n        Collections.sort(lstVersions);\n\n        for (int i = 0; i < lstVersions.size(); i++) {\n          VarVersionPair firstPair = new VarVersionPair(ent.getKey(), lstVersions.get(i));\n          VarType firstType = mapExprentMinTypes.get(firstPair);\n\n          if (firstPair.var == 0 && firstPair.version == 1 && !is_method_static) {\n            continue; // don't merge 'this' variable\n          }\n\n          for (int j = i + 1; j < lstVersions.size(); j++) {\n            VarVersionPair secondPair = new VarVersionPair(ent.getKey(), lstVersions.get(j));\n            VarType secondType = mapExprentMinTypes.get(secondPair);\n\n            if (firstType.equals(secondType) ||\n                firstType.equals(VarType.VARTYPE_NULL) && secondType.getType() == CodeConstants.TYPE_OBJECT ||\n                secondType.equals(VarType.VARTYPE_NULL) && firstType.getType() == CodeConstants.TYPE_OBJECT ||\n                firstType.getTypeFamily() == CodeConstants.TYPE_FAMILY_INTEGER && secondType.getTypeFamily() == CodeConstants.TYPE_FAMILY_INTEGER) {\n              VarType firstMaxType = mapExprentMaxTypes.get(firstPair);\n              VarType secondMaxType = mapExprentMaxTypes.get(secondPair);\n              VarType type = firstMaxType == null ? secondMaxType :\n                             secondMaxType == null ? firstMaxType :\n                             VarType.getCommonMinType(firstMaxType, secondMaxType);\n\n              if (firstType.getTypeFamily() == CodeConstants.TYPE_FAMILY_INTEGER && secondType.getTypeFamily() == CodeConstants.TYPE_FAMILY_INTEGER) {\n                type = switch (secondType.getType()) {\n                  case CodeConstants.TYPE_INT -> VarType.VARTYPE_INT;\n                  case CodeConstants.TYPE_SHORT -> firstType.getType() == CodeConstants.TYPE_INT ? null : VarType.VARTYPE_SHORT;\n                  case CodeConstants.TYPE_CHAR -> switch (firstType.getType()) {\n                    case CodeConstants.TYPE_INT, CodeConstants.TYPE_SHORT -> null;\n                    default -> VarType.VARTYPE_CHAR;\n                  };\n                  case CodeConstants.TYPE_SHORTCHAR -> switch (firstType.getType()) {\n                    case CodeConstants.TYPE_INT, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_CHAR -> null;\n                    default -> VarType.VARTYPE_SHORTCHAR;\n                  };\n                  case CodeConstants.TYPE_BYTECHAR -> switch (firstType.getType()) {\n                    case CodeConstants.TYPE_INT, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_SHORTCHAR -> null;\n                    default -> VarType.VARTYPE_BYTECHAR;\n                  };\n                  case CodeConstants.TYPE_BYTE -> switch (firstType.getType()) {\n                    case CodeConstants.TYPE_INT, CodeConstants.TYPE_SHORT, CodeConstants.TYPE_CHAR, CodeConstants.TYPE_SHORTCHAR, CodeConstants.TYPE_BYTECHAR ->\n                      null;\n                    default -> VarType.VARTYPE_BYTE;\n                  };\n                  default -> type;\n                };\n                if (type == null) {\n                  continue;\n                }\n                firstType = type;\n                mapExprentMinTypes.put(firstPair, type);\n              }\n\n              mapExprentMaxTypes.put(firstPair, type);\n              mapMergedVersions.put(secondPair, firstPair.version);\n              mapExprentMaxTypes.remove(secondPair);\n              mapExprentMinTypes.remove(secondPair);\n\n              if (firstType.equals(VarType.VARTYPE_NULL)) {\n                mapExprentMinTypes.put(firstPair, secondType);\n                firstType = secondType;\n              }\n\n              typeProcessor.getFinalVariables().put(firstPair, VarProcessor.VAR_NON_FINAL);\n\n              lstVersions.remove(j);\n              //noinspection AssignmentToForLoopParameter\n              j--;\n            }\n          }\n        }\n      }\n    }\n\n    if (!mapMergedVersions.isEmpty()) {\n      updateVersions(graph, mapMergedVersions);\n    }\n  }\n\n  private void setNewVarIndices(VarTypeProcessor typeProcessor, DirectGraph graph, VarVersionsProcessor previousVersionsProcessor) {\n    final Map<VarVersionPair, VarType> mapExprentMaxTypes = typeProcessor.getMaxExprentTypes();\n    Map<VarVersionPair, VarType> mapExprentMinTypes = typeProcessor.getMinExprentTypes();\n    Map<VarVersionPair, Integer> mapFinalVars = typeProcessor.getFinalVariables();\n\n    CounterContainer counters = DecompilerContext.getCounterContainer();\n\n    final Map<VarVersionPair, Integer> mapVarPaar = new HashMap<>();\n    Map<Integer, Integer> mapOriginalVarIndices = new HashMap<>();\n\n    // map var-version pairs on new var indexes\n    for (VarVersionPair pair : new ArrayList<>(mapExprentMinTypes.keySet())) {\n\n      if (pair.version >= 0) {\n        int newIndex = pair.version == 1 ? pair.var : counters.getCounterAndIncrement(CounterContainer.VAR_COUNTER);\n\n        VarVersionPair newVar = new VarVersionPair(newIndex, 0);\n\n        mapExprentMinTypes.put(newVar, mapExprentMinTypes.get(pair));\n        mapExprentMaxTypes.put(newVar, mapExprentMaxTypes.get(pair));\n\n        if (mapFinalVars.containsKey(pair)) {\n          mapFinalVars.put(newVar, mapFinalVars.remove(pair));\n        }\n\n        mapVarPaar.put(pair, newIndex);\n        mapOriginalVarIndices.put(newIndex, pair.var);\n      }\n    }\n\n    // set new vars\n    graph.iterateExprents(exprent -> {\n      List<Exprent> lst = exprent.getAllExprents(true);\n      lst.add(exprent);\n\n      for (Exprent expr : lst) {\n        if (expr.type == Exprent.EXPRENT_VAR) {\n          VarExprent newVar = (VarExprent)expr;\n          Integer newVarIndex = mapVarPaar.get(new VarVersionPair(newVar));\n          if (newVarIndex != null) {\n            newVar.setIndex(newVarIndex);\n            newVar.setVersion(0);\n          }\n        }\n        else if (expr.type == Exprent.EXPRENT_CONST) {\n          VarType maxType = mapExprentMaxTypes.get(new VarVersionPair(expr.id, -1));\n          if (maxType != null && maxType.equals(VarType.VARTYPE_CHAR)) {\n            ((ConstExprent)expr).setConstType(maxType);\n          }\n        }\n      }\n\n      return 0;\n    });\n\n    if (previousVersionsProcessor != null) {\n      Map<Integer, Integer> oldIndices = previousVersionsProcessor.getMapOriginalVarIndices();\n      this.mapOriginalVarIndices = new HashMap<>(mapOriginalVarIndices.size());\n      for (Entry<Integer, Integer> entry : mapOriginalVarIndices.entrySet()) {\n        Integer value = entry.getValue();\n        Integer oldValue = oldIndices.get(value);\n        value = oldValue != null ? oldValue : value;\n        this.mapOriginalVarIndices.put(entry.getKey(), value);\n      }\n    }\n    else {\n      this.mapOriginalVarIndices = mapOriginalVarIndices;\n    }\n  }\n\n  public VarType getVarType(VarVersionPair pair) {\n    return typeProcessor.getVarType(pair);\n  }\n\n  public void setVarType(VarVersionPair pair, VarType type) {\n    typeProcessor.setVarType(pair, type);\n  }\n\n  public int getVarFinal(VarVersionPair pair) {\n    Integer fin = typeProcessor.getFinalVariables().get(pair);\n    return fin == null ? VarProcessor.VAR_FINAL : fin;\n  }\n\n  public void setVarFinal(VarVersionPair pair, int finalType) {\n    typeProcessor.getFinalVariables().put(pair, finalType);\n  }\n\n  public Map<Integer, Integer> getMapOriginalVarIndices() {\n    return mapOriginalVarIndices;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.renamer;\n\nimport org.jetbrains.java.decompiler.struct.StructClass;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ClassWrapperNode {\n  private final StructClass classStruct;\n  private final List<ClassWrapperNode> subclasses = new ArrayList<>();\n\n  public ClassWrapperNode(StructClass cl) {\n    this.classStruct = cl;\n  }\n\n  public void addSubclass(ClassWrapperNode node) {\n    subclasses.add(node);\n  }\n\n  public StructClass getClassStruct() {\n    return classStruct;\n  }\n\n  public List<ClassWrapperNode> getSubclasses() {\n    return subclasses;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.modules.renamer;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Locale;\nimport java.util.Set;\n\npublic class ConverterHelper implements IIdentifierRenamer {\n  private static final Set<String> KEYWORDS = new HashSet<>(Arrays.asList(\n    \"abstract\", \"do\", \"if\", \"package\", \"synchronized\", \"boolean\", \"double\", \"implements\", \"private\", \"this\", \"break\", \"else\", \"import\",\n    \"protected\", \"throw\", \"byte\", \"extends\", \"instanceof\", \"public\", \"throws\", \"case\", \"false\", \"int\", \"return\", \"transient\", \"catch\",\n    \"final\", \"interface\", \"short\", \"true\", \"char\", \"finally\", \"long\", \"static\", \"try\", \"class\", \"float\", \"native\", \"strictfp\", \"void\",\n    \"const\", \"for\", \"new\", \"super\", \"volatile\", \"continue\", \"goto\", \"null\", \"switch\", \"while\", \"default\", \"assert\", \"enum\"));\n  private static final Set<String> RESERVED_WINDOWS_NAMESPACE = new HashSet<>(Arrays.asList(\n    \"con\", \"prn\", \"aux\", \"nul\",\n    \"com1\", \"com2\", \"com3\", \"com4\", \"com5\", \"com6\", \"com7\", \"com8\", \"com9\",\n    \"lpt1\", \"lpt2\", \"lpt3\", \"lpt4\", \"lpt5\", \"lpt6\", \"lpt7\", \"lpt8\", \"lpt9\"));\n\n  private int classCounter = 0;\n  private int fieldCounter = 0;\n  private int methodCounter = 0;\n  private final Set<String> setNonStandardClassNames = new HashSet<>();\n\n  @Override\n  public boolean toBeRenamed(Type elementType, String className, String element, String descriptor) {\n    String value = elementType == Type.ELEMENT_CLASS ? className : element;\n    return value == null ||\n           value.length() <= 2 ||\n           !isValidIdentifier(elementType == Type.ELEMENT_METHOD, value) ||\n           KEYWORDS.contains(value) ||\n           elementType == Type.ELEMENT_CLASS && (\n             RESERVED_WINDOWS_NAMESPACE.contains(value.toLowerCase(Locale.ENGLISH)) ||\n             value.length() > 255 - \".class\".length());\n  }\n\n  /**\n   * Return {@code true} if, and only if identifier passed is compliant to JLS9 section 3.8 AND DOES NOT CONTAINS so-called \"ignorable\" characters.\n   * Ignorable characters are removed by javac silently during compilation and thus may appear only in specially crafted obfuscated classes.\n   * For more information about \"ignorable\" characters see <a href=\"https://bugs.openjdk.org/browse/JDK-7144981\">JDK-7144981</a>.\n   *\n   * @param identifier Identifier to be checked\n   * @return {@code true} in case {@code identifier} passed can be used as an identifier; {@code false} otherwise.\n   */\n  private static boolean isValidIdentifier(boolean isMethod, String identifier) {\n\n    assert identifier != null : \"Null identifier passed to the isValidIdentifier() method.\";\n    assert !identifier.isEmpty() : \"Empty identifier passed to the isValidIdentifier() method.\";\n\n    if (isMethod && (identifier.equals(CodeConstants.INIT_NAME) || identifier.equals(CodeConstants.CLINIT_NAME))) {\n      return true;\n    }\n\n    if (!Character.isJavaIdentifierStart(identifier.charAt(0))) {\n      return false;\n    }\n\n    char[] chars = identifier.toCharArray();\n\n    for(int i = 1; i < chars.length; i++) {\n      char ch = chars[i];\n\n      if ((!Character.isJavaIdentifierPart(ch)) || Character.isIdentifierIgnorable(ch)) {\n        return false;\n      }\n    }\n\n    return true;\n\n  }\n\n  // TODO: consider possible conflicts with not renamed classes, fields and methods!\n  // We should get all relevant information here.\n  @Override\n  public String getNextClassName(String fullName, String shortName) {\n    if (shortName == null) {\n      return \"class_\" + (classCounter++);\n    }\n\n    int index = 0;\n    while (index < shortName.length() && Character.isDigit(shortName.charAt(index))) {\n      index++;\n    }\n\n    if (index == 0 || index == shortName.length()) {\n      return \"class_\" + (classCounter++);\n    }\n    else {\n      String name = shortName.substring(index);\n      if (setNonStandardClassNames.contains(name)) {\n        return \"Inner\" + name + \"_\" + (classCounter++);\n      }\n      else {\n        setNonStandardClassNames.add(name);\n        return \"Inner\" + name;\n      }\n    }\n  }\n\n  @Override\n  public String getNextFieldName(String className, String field, String descriptor) {\n    return \"field_\" + (fieldCounter++);\n  }\n\n  @Override\n  public String getNextMethodName(String className, String method, String descriptor) {\n    return \"method_\" + (methodCounter++);\n  }\n\n  // *****************************************************************************\n  // static methods\n  // *****************************************************************************\n\n  public static String getSimpleClassName(String fullName) {\n    return fullName.substring(fullName.lastIndexOf('/') + 1);\n  }\n\n  public static String replaceSimpleClassName(String fullName, String newName) {\n    return fullName.substring(0, fullName.lastIndexOf('/') + 1) + newName;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.renamer;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructContext;\nimport org.jetbrains.java.decompiler.struct.StructField;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.NewClassNameBuilder;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.io.IOException;\nimport java.util.*;\n\npublic class IdentifierConverter implements NewClassNameBuilder {\n  private final StructContext context;\n  private final IIdentifierRenamer helper;\n  private final PoolInterceptor interceptor;\n  private List<ClassWrapperNode> rootClasses = new ArrayList<>();\n  private List<ClassWrapperNode> rootInterfaces = new ArrayList<>();\n  private Map<String, Map<String, String>> interfaceNameMaps = new HashMap<>();\n\n  public IdentifierConverter(StructContext context, IIdentifierRenamer helper, PoolInterceptor interceptor) {\n    this.context = context;\n    this.helper = helper;\n    this.interceptor = interceptor;\n  }\n\n  public void rename() {\n    try {\n      buildInheritanceTree();\n      renameAllClasses();\n      renameInterfaces();\n      renameClasses();\n      context.reloadContext();\n    }\n    catch (IOException ex) {\n      throw new RuntimeException(\"Renaming failed!\");\n    }\n  }\n\n  private void renameClasses() {\n    List<ClassWrapperNode> lstClasses = getReversePostOrderListIterative(rootClasses);\n    Map<String, Map<String, String>> classNameMaps = new HashMap<>();\n\n    for (ClassWrapperNode node : lstClasses) {\n      StructClass cl = node.getClassStruct();\n      Map<String, String> names = new HashMap<>();\n\n      // merge information on super class\n      if (cl.superClass != null) {\n        Map<String, String> mapClass = classNameMaps.get(cl.superClass.getString());\n        if (mapClass != null) {\n          names.putAll(mapClass);\n        }\n      }\n\n      // merge information on interfaces\n      for (String ifName : cl.getInterfaceNames()) {\n        Map<String, String> mapInt = interfaceNameMaps.get(ifName);\n        if (mapInt != null) {\n          names.putAll(mapInt);\n        }\n        else {\n          StructClass clintr = context.getClass(ifName);\n          if (clintr != null) {\n            names.putAll(processExternalInterface(clintr));\n          }\n        }\n      }\n\n      renameClassIdentifiers(cl, names);\n\n      if (!node.getSubclasses().isEmpty()) {\n        classNameMaps.put(cl.qualifiedName, names);\n      }\n    }\n  }\n\n  private Map<String, String> processExternalInterface(StructClass cl) {\n    Map<String, String> names = new HashMap<>();\n\n    for (String ifName : cl.getInterfaceNames()) {\n      Map<String, String> mapInt = interfaceNameMaps.get(ifName);\n      if (mapInt != null) {\n        names.putAll(mapInt);\n      }\n      else {\n        StructClass clintr = context.getClass(ifName);\n        if (clintr != null) {\n          names.putAll(processExternalInterface(clintr));\n        }\n      }\n    }\n\n    renameClassIdentifiers(cl, names);\n\n    return names;\n  }\n\n  private void renameInterfaces() {\n    List<ClassWrapperNode> lstInterfaces = getReversePostOrderListIterative(rootInterfaces);\n    Map<String, Map<String, String>> interfaceNameMaps = new HashMap<>();\n\n    // rename methods and fields\n    for (ClassWrapperNode node : lstInterfaces) {\n\n      StructClass cl = node.getClassStruct();\n      Map<String, String> names = new HashMap<>();\n\n      // merge information on super interfaces\n      for (String ifName : cl.getInterfaceNames()) {\n        Map<String, String> mapInt = interfaceNameMaps.get(ifName);\n        if (mapInt != null) {\n          names.putAll(mapInt);\n        }\n      }\n\n      renameClassIdentifiers(cl, names);\n\n      interfaceNameMaps.put(cl.qualifiedName, names);\n    }\n\n    this.interfaceNameMaps = interfaceNameMaps;\n  }\n\n  private void renameAllClasses() {\n    // order not important\n    List<ClassWrapperNode> lstAllClasses = new ArrayList<>(getReversePostOrderListIterative(rootInterfaces));\n    lstAllClasses.addAll(getReversePostOrderListIterative(rootClasses));\n\n    // rename all interfaces and classes\n    for (ClassWrapperNode node : lstAllClasses) {\n      renameClass(node.getClassStruct());\n    }\n  }\n\n  private void renameClass(StructClass cl) {\n\n    if (!cl.isOwn()) {\n      return;\n    }\n\n    String classOldFullName = cl.qualifiedName;\n\n    // TODO: rename packages\n    String clSimpleName = ConverterHelper.getSimpleClassName(classOldFullName);\n    if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_CLASS, clSimpleName, null, null)) {\n      String classNewFullName;\n\n      do {\n        String classname = helper.getNextClassName(classOldFullName, ConverterHelper.getSimpleClassName(classOldFullName));\n        classNewFullName = ConverterHelper.replaceSimpleClassName(classOldFullName, classname);\n      }\n      while (context.getClasses().containsKey(classNewFullName));\n\n      interceptor.addName(classOldFullName, classNewFullName);\n    }\n  }\n\n  private void renameClassIdentifiers(StructClass cl, Map<String, String> names) {\n    // all classes are already renamed\n    String classOldFullName = cl.qualifiedName;\n    String classNewFullName = interceptor.getName(classOldFullName);\n\n    if (classNewFullName == null) {\n      classNewFullName = classOldFullName;\n    }\n\n    // methods\n    HashSet<String> setMethodNames = new HashSet<>();\n    for (StructMethod md : cl.getMethods()) {\n      setMethodNames.add(md.getName());\n    }\n\n    VBStyleCollection<StructMethod, String> methods = cl.getMethods();\n    for (int i = 0; i < methods.size(); i++) {\n\n      StructMethod mt = methods.get(i);\n      String key = methods.getKey(i);\n\n      boolean isPrivate = mt.hasModifier(CodeConstants.ACC_PRIVATE);\n\n      String name = mt.getName();\n      if (!cl.isOwn() || mt.hasModifier(CodeConstants.ACC_NATIVE)) {\n        // external and native methods must not be renamed\n        if (!isPrivate) {\n          names.put(key, name);\n        }\n      }\n      else if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_METHOD, classOldFullName, name, mt.getDescriptor())) {\n        if (isPrivate || !names.containsKey(key)) {\n          do {\n            name = helper.getNextMethodName(classOldFullName, name, mt.getDescriptor());\n          }\n          while (setMethodNames.contains(name));\n\n          if (!isPrivate) {\n            names.put(key, name);\n          }\n        }\n        else {\n          name = names.get(key);\n        }\n\n        interceptor.addName(classOldFullName + \" \" + mt.getName() + \" \" + mt.getDescriptor(),\n                            classNewFullName + \" \" + name + \" \" + buildNewDescriptor(false, mt.getDescriptor()));\n      }\n    }\n\n    // external fields are not being renamed\n    if (!cl.isOwn()) {\n      return;\n    }\n\n    // fields\n    // FIXME: should overloaded fields become the same name?\n    HashSet<String> setFieldNames = new HashSet<>();\n    for (StructField fd : cl.getFields()) {\n      setFieldNames.add(fd.getName());\n    }\n\n    for (StructField fd : cl.getFields()) {\n      if (helper.toBeRenamed(IIdentifierRenamer.Type.ELEMENT_FIELD, classOldFullName, fd.getName(), fd.getDescriptor())) {\n        String newName;\n        do {\n          newName = helper.getNextFieldName(classOldFullName, fd.getName(), fd.getDescriptor());\n        }\n        while (setFieldNames.contains(newName));\n\n        interceptor.addName(classOldFullName + \" \" + fd.getName() + \" \" + fd.getDescriptor(),\n                            classNewFullName + \" \" + newName + \" \" + buildNewDescriptor(true, fd.getDescriptor()));\n      }\n    }\n  }\n\n  @Override\n  public String buildNewClassname(String className) {\n    return interceptor.getName(className);\n  }\n\n  private String buildNewDescriptor(boolean isField, String descriptor) {\n    String newDescriptor;\n    if (isField) {\n      newDescriptor = FieldDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this);\n    }\n    else {\n      newDescriptor = MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this);\n    }\n    return newDescriptor != null ? newDescriptor : descriptor;\n  }\n\n  private static List<ClassWrapperNode> getReversePostOrderListIterative(List<ClassWrapperNode> roots) {\n    List<ClassWrapperNode> res = new ArrayList<>();\n\n    LinkedList<ClassWrapperNode> stackNode = new LinkedList<>();\n    LinkedList<Integer> stackIndex = new LinkedList<>();\n\n    Set<ClassWrapperNode> setVisited = new HashSet<>();\n\n    for (ClassWrapperNode root : roots) {\n      stackNode.add(root);\n      stackIndex.add(0);\n    }\n\n    while (!stackNode.isEmpty()) {\n      ClassWrapperNode node = stackNode.getLast();\n      int index = stackIndex.removeLast();\n\n      setVisited.add(node);\n\n      List<ClassWrapperNode> lstSubs = node.getSubclasses();\n\n      for (; index < lstSubs.size(); index++) {\n        ClassWrapperNode sub = lstSubs.get(index);\n        if (!setVisited.contains(sub)) {\n          stackIndex.add(index + 1);\n          stackNode.add(sub);\n          stackIndex.add(0);\n          break;\n        }\n      }\n\n      if (index == lstSubs.size()) {\n        res.add(0, node);\n        stackNode.removeLast();\n      }\n    }\n\n    return res;\n  }\n\n  private void buildInheritanceTree() {\n    Map<String, ClassWrapperNode> nodes = new HashMap<>();\n    Map<String, StructClass> classes = context.getClasses();\n\n    List<ClassWrapperNode> rootClasses = new ArrayList<>();\n    List<ClassWrapperNode> rootInterfaces = new ArrayList<>();\n\n    for (StructClass cl : classes.values()) {\n      if (!cl.isOwn()) {\n        continue;\n      }\n\n      LinkedList<StructClass> stack = new LinkedList<>();\n      LinkedList<ClassWrapperNode> stackSubNodes = new LinkedList<>();\n\n      stack.add(cl);\n      stackSubNodes.add(null);\n\n      while (!stack.isEmpty()) {\n        StructClass clStr = stack.removeFirst();\n        ClassWrapperNode child = stackSubNodes.removeFirst();\n\n        ClassWrapperNode node = nodes.get(clStr.qualifiedName);\n        boolean isNewNode = (node == null);\n\n        if (isNewNode) {\n          nodes.put(clStr.qualifiedName, node = new ClassWrapperNode(clStr));\n        }\n\n        if (child != null) {\n          node.addSubclass(child);\n        }\n\n        if (!isNewNode) {\n          break;\n        }\n        else {\n          boolean isInterface = clStr.hasModifier(CodeConstants.ACC_INTERFACE);\n          boolean found_parent = false;\n\n          if (isInterface) {\n            for (String ifName : clStr.getInterfaceNames()) {\n              StructClass clParent = classes.get(ifName);\n              if (clParent != null) {\n                stack.add(clParent);\n                stackSubNodes.add(node);\n                found_parent = true;\n              }\n            }\n          }\n          else if (clStr.superClass != null) { // null iff java/lang/Object\n            StructClass clParent = classes.get(clStr.superClass.getString());\n            if (clParent != null) {\n              stack.add(clParent);\n              stackSubNodes.add(node);\n              found_parent = true;\n            }\n          }\n\n          if (!found_parent) { // no super class or interface\n            (isInterface ? rootInterfaces : rootClasses).add(node);\n          }\n        }\n      }\n    }\n\n    this.rootClasses = rootClasses;\n    this.rootInterfaces = rootInterfaces;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.modules.renamer;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class PoolInterceptor {\n  private final Map<String, String> mapOldToNewNames = new HashMap<>();\n  private final Map<String, String> mapNewToOldNames = new HashMap<>();\n\n  public void addName(String oldName, String newName) {\n    mapOldToNewNames.put(oldName, newName);\n    mapNewToOldNames.put(newName, oldName);\n  }\n\n  public String getName(String oldName) {\n    return mapOldToNewNames.get(oldName);\n  }\n\n  public String getOldName(String newName) {\n    return mapNewToOldNames.get(newName);\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/ContextUnit.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.main.extern.IResultSaver;\nimport org.jetbrains.java.decompiler.struct.lazy.LazyLoader;\nimport org.jetbrains.java.decompiler.struct.lazy.LazyLoader.Link;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.jar.JarFile;\nimport java.util.jar.Manifest;\n\npublic class ContextUnit {\n\n  public static final int TYPE_FOLDER = 0;\n  public static final int TYPE_JAR = 1;\n  public static final int TYPE_ZIP = 2;\n\n  private final int type;\n  private final boolean own;\n\n  private final String archivePath;  // relative path to jar/zip\n  private final String filename;     // folder: relative path, archive: file name\n  private final IResultSaver resultSaver;\n  private final IDecompiledData decompiledData;\n\n  private final List<String> classEntries = new ArrayList<>();  // class file or jar/zip entry\n  private final List<String> dirEntries = new ArrayList<>();\n  private final List<String[]> otherEntries = new ArrayList<>();\n\n  private List<StructClass> classes = new ArrayList<>();\n  private Manifest manifest;\n\n  public ContextUnit(int type, String archivePath, String filename, boolean own, IResultSaver resultSaver, IDecompiledData decompiledData) {\n    this.type = type;\n    this.own = own;\n    this.archivePath = archivePath;\n    this.filename = filename;\n    this.resultSaver = resultSaver;\n    this.decompiledData = decompiledData;\n  }\n\n  public void addClass(StructClass cl, String entryName) {\n    classes.add(cl);\n    classEntries.add(entryName);\n  }\n\n  public void addDirEntry(String entry) {\n    dirEntries.add(entry);\n  }\n\n  public void addOtherEntry(String fullPath, String entry) {\n    otherEntries.add(new String[]{fullPath, entry});\n  }\n\n  public void reload(LazyLoader loader) throws IOException {\n    List<StructClass> lstClasses = new ArrayList<>();\n\n    for (StructClass cl : classes) {\n      String oldName = cl.qualifiedName;\n\n      StructClass newCl;\n      try (DataInputFullStream in = loader.getClassStream(oldName)) {\n        newCl = StructClass.create(in, cl.isOwn(), loader);\n      }\n\n      lstClasses.add(newCl);\n\n      Link lnk = loader.getClassLink(oldName);\n      loader.removeClassLink(oldName);\n      loader.addClassLink(newCl.qualifiedName, lnk);\n    }\n\n    classes = lstClasses;\n  }\n\n  public void save() {\n    switch (type) {\n      case TYPE_FOLDER -> {\n        // create folder\n        resultSaver.saveFolder(filename);\n\n        // non-class files\n        for (String[] pair : otherEntries) {\n          resultSaver.copyFile(pair[0], filename, pair[1]);\n        }\n\n        // classes\n        for (int i = 0; i < classes.size(); i++) {\n          StructClass cl = classes.get(i);\n          if (!cl.isOwn()) {\n            continue;\n          }\n          String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i));\n          if (entryName != null) {\n            String content = decompiledData.getClassContent(cl);\n            if (content != null) {\n              int[] mapping = null;\n              if (DecompilerContext.getOption(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING)) {\n                mapping = DecompilerContext.getBytecodeSourceMapper().getOriginalLinesMapping();\n              }\n              resultSaver.saveClassFile(filename, cl.qualifiedName, entryName, content, mapping);\n            }\n          }\n        }\n      }\n      case TYPE_JAR, TYPE_ZIP -> {\n        // create archive file\n        resultSaver.saveFolder(archivePath);\n        resultSaver.createArchive(archivePath, filename, manifest);\n\n        // directory entries\n        for (String dirEntry : dirEntries) {\n          resultSaver.saveDirEntry(archivePath, filename, dirEntry);\n        }\n\n        // non-class entries\n        for (String[] pair : otherEntries) {\n          if (type != TYPE_JAR || !JarFile.MANIFEST_NAME.equalsIgnoreCase(pair[1])) {\n            resultSaver.copyEntry(pair[0], archivePath, filename, pair[1]);\n          }\n        }\n\n        // classes\n        for (int i = 0; i < classes.size(); i++) {\n          StructClass cl = classes.get(i);\n          String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i));\n          if (entryName != null) {\n            String content = decompiledData.getClassContent(cl);\n            resultSaver.saveClassEntry(archivePath, filename, cl.qualifiedName, entryName, content);\n          }\n        }\n\n        resultSaver.closeArchive(archivePath, filename);\n      }\n    }\n  }\n\n  public void setManifest(Manifest manifest) {\n    this.manifest = manifest;\n  }\n\n  public boolean isOwn() {\n    return own;\n  }\n\n  public List<StructClass> getClasses() {\n    return classes;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/IDecompiledData.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct;\n\npublic interface IDecompiledData {\n\n  String getClassEntryName(StructClass cl, String entryname);\n\n  String getClassContent(StructClass cl);\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/StructClass.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructPermittedSubclassesAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructRecordAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.Type;\nimport org.jetbrains.java.decompiler.struct.lazy.LazyLoader;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Map;\n\n/*\n  class_file {\n    u4 magic;\n    u2 minor_version;\n    u2 major_version;\n    u2 constant_pool_count;\n    cp_info constant_pool[constant_pool_count-1];\n    u2 access_flags;\n    u2 this_class;\n    u2 super_class;\n    u2 interfaces_count;\n    u2 interfaces[interfaces_count];\n    u2 fields_count;\n    field_info fields[fields_count];\n    u2 methods_count;\n    method_info methods[methods_count];\n    u2 attributes_count;\n    attribute_info attributes[attributes_count];\n  }\n*/\npublic class StructClass extends StructMember {\n  public static StructClass create(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException {\n    in.discard(4);\n    int minorVersion = in.readUnsignedShort();\n    int majorVersion = in.readUnsignedShort();\n    int bytecodeVersion = Math.max(majorVersion, CodeConstants.BYTECODE_JAVA_LE_4);\n\n    ConstantPool pool = new ConstantPool(in);\n\n    int accessFlags = in.readUnsignedShort();\n    int thisClassIdx = in.readUnsignedShort();\n    int superClassIdx = in.readUnsignedShort();\n    String qualifiedName = pool.getPrimitiveConstant(thisClassIdx).getString();\n    PrimitiveConstant superClass = pool.getPrimitiveConstant(superClassIdx);\n\n    int length = in.readUnsignedShort();\n    int[] interfaces = new int[length];\n    String[] interfaceNames = new String[length];\n    for (int i = 0; i < length; i++) {\n      interfaces[i] = in.readUnsignedShort();\n      interfaceNames[i] = pool.getPrimitiveConstant(interfaces[i]).getString();\n    }\n\n    length = in.readUnsignedShort();\n    VBStyleCollection<StructField, String>fields = new VBStyleCollection<>(length);\n    for (int i = 0; i < length; i++) {\n      StructField field = StructField.create(in, pool, qualifiedName);\n      fields.addWithKey(field, InterpreterUtil.makeUniqueKey(field.getName(), field.getDescriptor()));\n    }\n\n    length = in.readUnsignedShort();\n    VBStyleCollection<StructMethod, String>methods = new VBStyleCollection<>(length);\n    for (int i = 0; i < length; i++) {\n      StructMethod method = StructMethod.create(in, pool, qualifiedName, bytecodeVersion, own);\n      methods.addWithKey(method, InterpreterUtil.makeUniqueKey(method.getName(), method.getDescriptor()));\n    }\n\n    Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);\n\n    StructClass cl = new StructClass(\n      accessFlags, attributes, qualifiedName, superClass, own, loader, minorVersion, majorVersion, interfaces, interfaceNames, fields, methods);\n    if (loader == null) cl.pool = pool;\n    return cl;\n  }\n\n  public final String qualifiedName;\n  public final PrimitiveConstant superClass;\n  private final boolean own;\n  private final LazyLoader loader;\n  private final int minorVersion;\n  private final int majorVersion;\n  private final int[] interfaces;\n  private final String[] interfaceNames;\n  private final VBStyleCollection<StructField, String> fields;\n  private final VBStyleCollection<StructMethod, String> methods;\n\n  private ConstantPool pool;\n\n  private StructClass(int accessFlags,\n                      Map<String, StructGeneralAttribute> attributes,\n                      String qualifiedName,\n                      PrimitiveConstant superClass,\n                      boolean own,\n                      LazyLoader loader,\n                      int minorVersion,\n                      int majorVersion,\n                      int[] interfaces,\n                      String[] interfaceNames,\n                      VBStyleCollection<StructField, String> fields,\n                      VBStyleCollection<StructMethod, String> methods) {\n    super(accessFlags, attributes);\n    this.qualifiedName = qualifiedName;\n    this.superClass = superClass;\n    this.own = own;\n    this.loader = loader;\n    this.minorVersion = minorVersion;\n    this.majorVersion = majorVersion;\n    this.interfaces = interfaces;\n    this.interfaceNames = interfaceNames;\n    this.fields = fields;\n    this.methods = methods;\n  }\n\n  public boolean hasField(String name, String descriptor) {\n    return getField(name, descriptor) != null;\n  }\n\n  public StructField getField(String name, String descriptor) {\n    return fields.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));\n  }\n\n  public StructMethod getMethod(String key) {\n    return methods.getWithKey(key);\n  }\n\n  public StructMethod getMethod(String name, String descriptor) {\n    return methods.getWithKey(InterpreterUtil.makeUniqueKey(name, descriptor));\n  }\n\n  public String getInterface(int i) {\n    return interfaceNames[i];\n  }\n\n  public void releaseResources() {\n    if (loader != null) {\n      pool = null;\n    }\n  }\n\n  public ConstantPool getPool() {\n    if (pool == null && loader != null) {\n      pool = loader.loadPool(qualifiedName);\n    }\n    return pool;\n  }\n\n  /**\n   * @return list of record components; null if this class is not a record\n   */\n  public List<StructRecordComponent> getRecordComponents() {\n    StructRecordAttribute recordAttr = getAttribute(StructGeneralAttribute.ATTRIBUTE_RECORD);\n    if (recordAttr == null) return null;\n    return recordAttr.getComponents();\n  }\n\n  public List<String> getPermittedSubclasses() {\n    StructPermittedSubclassesAttribute permittedSubClassAttr = getAttribute(StructGeneralAttribute.ATTRIBUTE_PERMITTED_SUBCLASSES);\n    if (permittedSubClassAttr == null) return null;\n    return permittedSubClassAttr.getClasses();\n  }\n\n  public int[] getInterfaces() {\n    return interfaces;\n  }\n\n  public String[] getInterfaceNames() {\n    return interfaceNames;\n  }\n\n  public VBStyleCollection<StructMethod, String> getMethods() {\n    return methods;\n  }\n\n  public VBStyleCollection<StructField, String> getFields() {\n    return fields;\n  }\n\n  public boolean isOwn() {\n    return own;\n  }\n\n  public LazyLoader getLoader() {\n    return loader;\n  }\n\n  public boolean isVersion5() {\n    return majorVersion > CodeConstants.BYTECODE_JAVA_LE_4 ||\n           majorVersion == CodeConstants.BYTECODE_JAVA_LE_4 && minorVersion > 0; // FIXME: check second condition\n  }\n\n  public boolean isVersion7() {\n    return majorVersion >= CodeConstants.BYTECODE_JAVA_7;\n  }\n\n  public boolean isVersion8() {\n    return majorVersion >= CodeConstants.BYTECODE_JAVA_8;\n  }\n\n  public boolean isVersion14() {\n    return majorVersion >= CodeConstants.BYTECODE_JAVA_14;\n  }\n\n  public boolean isVersion15() {\n    return majorVersion >= CodeConstants.BYTECODE_JAVA_15;\n  }\n\n  public boolean isVersion16() {\n    return majorVersion >= CodeConstants.BYTECODE_JAVA_16;\n  }\n\n  public boolean isVersion17() {\n    return majorVersion >= CodeConstants.BYTECODE_JAVA_17;\n  }\n\n  public boolean isVersion21() {\n    return majorVersion >= CodeConstants.BYTECODE_JAVA_21;\n  }\n\n  public boolean isPreviewVersion() {\n    return minorVersion == 0xFFFF;\n  }\n\n  public boolean hasSealedClassesSupport() {\n    return isVersion17() || isVersion15() && isPreviewVersion();\n  }\n\n  public boolean hasPatternsInInstanceofSupport() {\n    return isVersion16() || isVersion14() && isPreviewVersion();\n  }\n\n  public boolean hasEnhancedSwitchSupport() {\n    return isVersion14();\n  }\n\n  public boolean hasRecordPatternSupport() {\n    return isVersion21();\n  }\n\n  @Override\n  public String toString() {\n    return qualifiedName;\n  }\n\n  @Override\n  protected Type getType() {\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/StructContext.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IResultSaver;\nimport org.jetbrains.java.decompiler.struct.lazy.LazyLoader;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.jar.JarFile;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\npublic class StructContext {\n  private final IResultSaver saver;\n  private final IDecompiledData decompiledData;\n  private final LazyLoader loader;\n  private final Map<String, ContextUnit> units = new HashMap<>();\n  private final Map<String, StructClass> classes = new HashMap<>();\n\n  public StructContext(IResultSaver saver, IDecompiledData decompiledData, LazyLoader loader) {\n    this.saver = saver;\n    this.decompiledData = decompiledData;\n    this.loader = loader;\n\n    ContextUnit defaultUnit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, \"\", true, saver, decompiledData);\n    units.put(\"\", defaultUnit);\n  }\n\n  public StructClass getClass(String name) {\n    return classes.get(name);\n  }\n\n  public void reloadContext() throws IOException {\n    for (ContextUnit unit : units.values()) {\n      for (StructClass cl : unit.getClasses()) {\n        classes.remove(cl.qualifiedName);\n      }\n\n      unit.reload(loader);\n\n      // adjust global class collection\n      for (StructClass cl : unit.getClasses()) {\n        classes.put(cl.qualifiedName, cl);\n      }\n    }\n  }\n\n  public void saveContext() {\n    for (ContextUnit unit : units.values()) {\n      if (unit.isOwn()) {\n        unit.save();\n      }\n    }\n  }\n\n  public void addSpace(File file, boolean isOwn) {\n    addSpace(\"\", file, isOwn, 0);\n  }\n\n  private void addSpace(String path, File file, boolean isOwn, int level) {\n    if (file.isDirectory()) {\n      if (level == 1) path += file.getName();\n      else if (level > 1) path += \"/\" + file.getName();\n\n      File[] files = file.listFiles();\n      if (files != null) {\n        for (int i = files.length - 1; i >= 0; i--) {\n          addSpace(path, files[i], isOwn, level + 1);\n        }\n      }\n    }\n    else {\n      String filename = file.getName();\n\n      boolean isArchive = false;\n      try {\n        if (filename.endsWith(\".jar\")) {\n          isArchive = true;\n          addArchive(path, file, ContextUnit.TYPE_JAR, isOwn);\n        }\n        else if (filename.endsWith(\".zip\")) {\n          isArchive = true;\n          addArchive(path, file, ContextUnit.TYPE_ZIP, isOwn);\n        }\n      }\n      catch (IOException ex) {\n        String message = \"Corrupted archive file: \" + file;\n        DecompilerContext.getLogger().writeMessage(message, ex);\n      }\n      if (isArchive) {\n        return;\n      }\n\n      ContextUnit unit = units.get(path);\n      if (unit == null) {\n        unit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, path, isOwn, saver, decompiledData);\n        units.put(path, unit);\n      }\n\n      if (filename.endsWith(\".class\")) {\n        try (DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null)) {\n          StructClass cl = StructClass.create(in, isOwn, loader);\n          classes.put(cl.qualifiedName, cl);\n          unit.addClass(cl, filename);\n          loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), null));\n        }\n        catch (IOException ex) {\n          String message = \"Corrupted class file: \" + file;\n          DecompilerContext.getLogger().writeMessage(message, ex);\n        }\n      }\n      else {\n        unit.addOtherEntry(file.getAbsolutePath(), filename);\n      }\n    }\n  }\n\n  private void addArchive(String path, File file, int type, boolean isOwn) throws IOException {\n    try (ZipFile archive = type == ContextUnit.TYPE_JAR ? new JarFile(file) : new ZipFile(file)) {\n      Enumeration<? extends ZipEntry> entries = archive.entries();\n      while (entries.hasMoreElements()) {\n        ZipEntry entry = entries.nextElement();\n        if (entry.getName().startsWith(\"META-INF/versions\")) continue; // workaround for multi release Jars (see IDEA-285079)\n        ContextUnit unit = units.get(path + \"/\" + file.getName());\n        if (unit == null) {\n          unit = new ContextUnit(type, path, file.getName(), isOwn, saver, decompiledData);\n          if (type == ContextUnit.TYPE_JAR) {\n            unit.setManifest(((JarFile)archive).getManifest());\n          }\n          units.put(path + \"/\" + file.getName(), unit);\n        }\n\n        String name = entry.getName();\n        File test = new File(file.getAbsolutePath(), name);\n        if (!test.getCanonicalPath().startsWith(file.getCanonicalPath() + File.separator)) { // check for zip slip exploit\n          throw new RuntimeException(\"Zip entry '\" + entry.getName() + \"' tries to escape target directory\");\n        }\n\n        if (!entry.isDirectory()) {\n          if (name.endsWith(\".class\")) {\n            byte[] bytes = InterpreterUtil.getBytes(archive, entry);\n            StructClass cl = StructClass.create(new DataInputFullStream(bytes), isOwn, loader);\n            classes.put(cl.qualifiedName, cl);\n            unit.addClass(cl, name);\n            loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(file.getAbsolutePath(), name));\n          }\n          else {\n            unit.addOtherEntry(file.getAbsolutePath(), name);\n          }\n        }\n        else {\n          unit.addDirEntry(name);\n        }\n      }\n    }\n  }\n\n  public Map<String, StructClass> getClasses() {\n    return classes;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/StructField.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct;\n\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.gen.Type;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.Map;\n\n/*\n  field_info {\n    u2 access_flags;\n    u2 name_index;\n    u2 descriptor_index;\n    u2 attributes_count;\n    attribute_info attributes[attributes_count];\n   }\n*/\npublic class StructField extends StructMember {\n  public static StructField create(DataInputFullStream in, ConstantPool pool, String clQualifiedName) throws IOException {\n    int accessFlags = in.readUnsignedShort();\n    int nameIndex = in.readUnsignedShort();\n    int descriptorIndex = in.readUnsignedShort();\n\n    String[] values = pool.getClassElement(ConstantPool.FIELD, clQualifiedName, nameIndex, descriptorIndex);\n\n    Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);\n\n    return new StructField(accessFlags, attributes, values[0], values[1]);\n  }\n\n  private final String name;\n  private final String descriptor;\n\n  protected StructField(int accessFlags, Map<String, StructGeneralAttribute> attributes, String name, String descriptor) {\n    super(accessFlags, attributes);\n    this.name = name;\n    this.descriptor = descriptor;\n  }\n\n  public final String getName() {\n    return name;\n  }\n\n  public final String getDescriptor() {\n    return descriptor;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n\n  @Override\n  protected Type getType() {\n    return new VarType(descriptor);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/StructMember.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TargetInfo;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TypeAnnotation;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTypeTableAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructTypeAnnotationAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.gen.Type;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\npublic abstract class StructMember {\n  private final int accessFlags;\n  private final Map<String, StructGeneralAttribute> attributes;\n\n  protected StructMember(int accessFlags, Map<String, StructGeneralAttribute> attributes) {\n    this.accessFlags = accessFlags;\n    this.attributes = attributes;\n  }\n\n  public int getAccessFlags() {\n    return accessFlags;\n  }\n\n  public <T extends StructGeneralAttribute> T getAttribute(StructGeneralAttribute.Key<T> attribute) {\n    @SuppressWarnings(\"unchecked\") T t = (T)attributes.get(attribute.name);\n    return t;\n  }\n\n  public boolean hasAttribute(StructGeneralAttribute.Key<?> attribute) {\n    return attributes.containsKey(attribute.name);\n  }\n\n  public boolean hasModifier(int modifier) {\n    return (accessFlags & modifier) == modifier;\n  }\n\n  public boolean isSynthetic() {\n    return hasModifier(CodeConstants.ACC_SYNTHETIC) || hasAttribute(StructGeneralAttribute.ATTRIBUTE_SYNTHETIC);\n  }\n\n  protected abstract Type getType();\n\n  /**\n   * Checks whether annotations should go on type instead of member\n   */\n  public boolean memberAnnCollidesWithTypeAnnotation(AnnotationExprent typeAnnotationExpr) {\n    Type type = getType();\n    if (type == null) return false; // when there is no type reference no collision is possible\n    Set<AnnotationExprent> typeAnnotations = TargetInfo.EmptyTarget.extract(getPossibleTypeAnnotationCollisions(type))\n      .stream()\n      .map(typeAnnotation-> typeAnnotation.getAnnotationExpr())\n      .collect(Collectors.toUnmodifiableSet());\n    return typeAnnotations.contains(typeAnnotationExpr);\n  }\n\n  /**\n   * Checks whether annotations should go on parameter type instead of parameter\n   */\n  public boolean paramAnnCollidesWithTypeAnnotation(AnnotationExprent typeAnnotationExpr, @NotNull Type type, int param) {\n    Set<AnnotationExprent> typeAnnotations = TargetInfo.FormalParameterTarget\n      .extract(getPossibleTypeAnnotationCollisions(type), param).stream()\n      .map(typeAnnotation-> typeAnnotation.getAnnotationExpr())\n      .collect(Collectors.toUnmodifiableSet());\n    return typeAnnotations.contains(typeAnnotationExpr);\n  }\n\n  private List<TypeAnnotation> getPossibleTypeAnnotationCollisions(@NotNull Type type) {\n    return Arrays.stream(StructGeneralAttribute.TYPE_ANNOTATION_ATTRIBUTES)\n      .flatMap(attrKey -> {\n        StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute)getAttribute(attrKey);\n        if (attribute == null) {\n          return Stream.empty();\n        } else {\n          return attribute.getAnnotations().stream();\n        }\n      })\n      .filter(ta -> ta.isWrittenBeforeType(type))\n      .collect(Collectors.toList());\n  }\n\n  public static Map<String, StructGeneralAttribute> readAttributes(DataInputFullStream in, ConstantPool pool) throws IOException {\n    int length = in.readUnsignedShort();\n    Map<String, StructGeneralAttribute> attributes = new HashMap<>(length);\n\n    for (int i = 0; i < length; i++) {\n      int nameIndex = in.readUnsignedShort();\n      String name = pool.getPrimitiveConstant(nameIndex).getString();\n\n      StructGeneralAttribute attribute = StructGeneralAttribute.createAttribute(name);\n      int attLength = in.readInt();\n      if (attribute == null) {\n        in.discard(attLength);\n      }\n      else {\n        attribute.initContent(in, pool);\n        if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name) && attributes.containsKey(name)) {\n          // merge all variable tables\n          StructLocalVariableTableAttribute table = (StructLocalVariableTableAttribute)attributes.get(name);\n          table.add((StructLocalVariableTableAttribute)attribute);\n        }\n        else if (StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name) && attributes.containsKey(name)) {\n          // merge all variable tables\n          StructLocalVariableTypeTableAttribute table = (StructLocalVariableTypeTableAttribute)attributes.get(name);\n          table.add((StructLocalVariableTypeTableAttribute)attribute);\n        }\n        else {\n          attributes.put(name, attribute);\n        }\n      }\n    }\n\n    return attributes;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/StructMethod.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct;\n\nimport org.jetbrains.java.decompiler.code.*;\nimport org.jetbrains.java.decompiler.struct.attr.StructCodeAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.Type;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\nimport org.jetbrains.java.decompiler.util.VBStyleCollection;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.jetbrains.java.decompiler.code.CodeConstants.*;\n\n/*\n  method_info {\n    u2 access_flags;\n    u2 name_index;\n    u2 descriptor_index;\n    u2 attributes_count;\n    attribute_info attributes[attributes_count];\n  }\n*/\npublic class StructMethod extends StructMember {\n  public static StructMethod create(DataInputFullStream in, ConstantPool pool, String clQualifiedName, int bytecodeVersion, boolean own) throws IOException {\n    int accessFlags = in.readUnsignedShort();\n    int nameIndex = in.readUnsignedShort();\n    int descriptorIndex = in.readUnsignedShort();\n\n    String[] values = pool.getClassElement(ConstantPool.METHOD, clQualifiedName, nameIndex, descriptorIndex);\n\n    Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);\n    StructCodeAttribute code = (StructCodeAttribute)attributes.remove(StructGeneralAttribute.ATTRIBUTE_CODE.name);\n    if (code != null) {\n      attributes.putAll(code.codeAttributes);\n    }\n\n    return new StructMethod(accessFlags, attributes, values[0], values[1], bytecodeVersion, own ? code : null);\n  }\n\n  private static final int[] opr_iconst = {-1, 0, 1, 2, 3, 4, 5};\n  private static final int[] opr_loadstore = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3};\n  private static final int[] opcs_load = {opc_iload, opc_lload, opc_fload, opc_dload, opc_aload};\n  private static final int[] opcs_store = {opc_istore, opc_lstore, opc_fstore, opc_dstore, opc_astore};\n\n  private final String name;\n  private final String descriptor;\n  private final int bytecodeVersion;\n  private final int localVariables;\n  private final int codeLength;\n  private final int codeFullLength;\n  private InstructionSequence seq = null;\n  private boolean expanded = false;\n\n  private StructMethod(int accessFlags,\n                       Map<String, StructGeneralAttribute> attributes,\n                       String name,\n                       String descriptor,\n                       int bytecodeVersion,\n                       StructCodeAttribute code) {\n    super(accessFlags, attributes);\n    this.name = name;\n    this.descriptor = descriptor;\n    this.bytecodeVersion = bytecodeVersion;\n    if (code != null) {\n      this.localVariables = code.localVariables;\n      this.codeLength = code.codeLength;\n      this.codeFullLength = code.codeFullLength;\n    }\n    else {\n      this.localVariables = this.codeLength = this.codeFullLength = -1;\n    }\n  }\n\n  public void expandData(StructClass classStruct) throws IOException {\n    if (codeLength >= 0 && !expanded) {\n      byte[] code = classStruct.getLoader().loadBytecode(classStruct, this, codeFullLength);\n      seq = parseBytecode(new DataInputFullStream(code), codeLength, classStruct.getPool());\n      expanded = true;\n    }\n  }\n\n  public void releaseResources() {\n    if (codeLength >= 0 && expanded) {\n      seq = null;\n      expanded = false;\n    }\n  }\n\n  @SuppressWarnings(\"AssignmentToForLoopParameter\")\n  private InstructionSequence parseBytecode(DataInputFullStream in, int length, ConstantPool pool) throws IOException {\n    VBStyleCollection<Instruction, Integer> instructions = new VBStyleCollection<>();\n\n    for (int i = 0; i < length; ) {\n      int offset = i;\n\n      int opcode = in.readUnsignedByte();\n      int group = GROUP_GENERAL;\n\n      boolean wide = (opcode == opc_wide);\n\n      if (wide) {\n        i++;\n        opcode = in.readUnsignedByte();\n      }\n\n      List<Integer> operands = new ArrayList<>();\n\n      if (opcode >= opc_iconst_m1 && opcode <= opc_iconst_5) {\n        operands.add(opr_iconst[opcode - opc_iconst_m1]);\n        opcode = opc_bipush;\n      }\n      else if (opcode >= opc_iload_0 && opcode <= opc_aload_3) {\n        operands.add(opr_loadstore[opcode - opc_iload_0]);\n        opcode = opcs_load[(opcode - opc_iload_0) / 4];\n      }\n      else if (opcode >= opc_istore_0 && opcode <= opc_astore_3) {\n        operands.add(opr_loadstore[opcode - opc_istore_0]);\n        opcode = opcs_store[(opcode - opc_istore_0) / 4];\n      }\n      else {\n        switch (opcode) {\n          case opc_bipush -> {\n            operands.add((int)in.readByte());\n            i++;\n          }\n          case opc_ldc, opc_newarray -> {\n            operands.add(in.readUnsignedByte());\n            i++;\n          }\n          case opc_sipush, opc_ifeq, opc_ifne, opc_iflt, opc_ifge, opc_ifgt, opc_ifle, opc_if_icmpeq, opc_if_icmpne, opc_if_icmplt,\n            opc_if_icmpge, opc_if_icmpgt, opc_if_icmple, opc_if_acmpeq, opc_if_acmpne, opc_goto, opc_jsr, opc_ifnull, opc_ifnonnull -> {\n            if (opcode != opc_sipush) {\n              group = GROUP_JUMP;\n            }\n            operands.add((int)in.readShort());\n            i += 2;\n          }\n          case opc_ldc_w, opc_ldc2_w, opc_getstatic, opc_putstatic, opc_getfield, opc_putfield, opc_invokevirtual, opc_invokespecial,\n            opc_invokestatic, opc_new, opc_anewarray, opc_checkcast, opc_instanceof -> {\n            operands.add(in.readUnsignedShort());\n            i += 2;\n            if (opcode >= opc_getstatic && opcode <= opc_putfield) {\n              group = GROUP_FIELDACCESS;\n            }\n            else if (opcode >= opc_invokevirtual && opcode <= opc_invokestatic) {\n              group = GROUP_INVOCATION;\n            }\n          }\n          case opc_invokedynamic -> {\n            if (bytecodeVersion >= CodeConstants.BYTECODE_JAVA_7) { // instruction unused in Java 6 and before\n              operands.add(in.readUnsignedShort());\n              in.discard(2);\n              group = GROUP_INVOCATION;\n              i += 4;\n            }\n          }\n          case opc_iload, opc_lload, opc_fload, opc_dload, opc_aload, opc_istore, opc_lstore,\n            opc_fstore, opc_dstore, opc_astore, opc_ret -> {\n            if (wide) {\n              operands.add(in.readUnsignedShort());\n              i += 2;\n            }\n            else {\n              operands.add(in.readUnsignedByte());\n              i++;\n            }\n            if (opcode == opc_ret) {\n              group = GROUP_RETURN;\n            }\n          }\n          case opc_iinc -> {\n            if (wide) {\n              operands.add(in.readUnsignedShort());\n              operands.add((int)in.readShort());\n              i += 4;\n            }\n            else {\n              operands.add(in.readUnsignedByte());\n              operands.add((int)in.readByte());\n              i += 2;\n            }\n          }\n          case opc_goto_w, opc_jsr_w -> {\n            opcode = opcode == opc_jsr_w ? opc_jsr : opc_goto;\n            operands.add(in.readInt());\n            group = GROUP_JUMP;\n            i += 4;\n          }\n          case opc_invokeinterface -> {\n            operands.add(in.readUnsignedShort());\n            operands.add(in.readUnsignedByte());\n            in.discard(1);\n            group = GROUP_INVOCATION;\n            i += 4;\n          }\n          case opc_multianewarray -> {\n            operands.add(in.readUnsignedShort());\n            operands.add(in.readUnsignedByte());\n            i += 3;\n          }\n          case opc_tableswitch -> {\n            in.discard((4 - (i + 1) % 4) % 4);\n            i += ((4 - (i + 1) % 4) % 4); // padding\n            operands.add(in.readInt());\n            i += 4;\n            int low = in.readInt();\n            operands.add(low);\n            i += 4;\n            int high = in.readInt();\n            operands.add(high);\n            i += 4;\n\n            for (int j = 0; j < high - low + 1; j++) {\n              operands.add(in.readInt());\n              i += 4;\n            }\n            group = GROUP_SWITCH;\n          }\n          case opc_lookupswitch -> {\n            in.discard((4 - (i + 1) % 4) % 4);\n            i += ((4 - (i + 1) % 4) % 4); // padding\n            operands.add(in.readInt());\n            i += 4;\n            int npairs = in.readInt();\n            operands.add(npairs);\n            i += 4;\n\n            for (int j = 0; j < npairs; j++) {\n              operands.add(in.readInt());\n              i += 4;\n              operands.add(in.readInt());\n              i += 4;\n            }\n            group = GROUP_SWITCH;\n          }\n          case opc_ireturn, opc_lreturn, opc_freturn, opc_dreturn, opc_areturn, opc_return, opc_athrow ->\n            group = GROUP_RETURN;\n        }\n      }\n\n      int[] ops = null;\n      if (!operands.isEmpty()) {\n        ops = new int[operands.size()];\n        for (int j = 0; j < operands.size(); j++) {\n          ops[j] = operands.get(j);\n        }\n      }\n\n      Instruction instr = Instruction.create(opcode, wide, group, bytecodeVersion, ops);\n\n      instructions.addWithKey(instr, offset);\n\n      i++;\n    }\n\n    // initialize exception table\n    List<ExceptionHandler> lstHandlers = new ArrayList<>();\n\n    int exception_count = in.readUnsignedShort();\n    for (int i = 0; i < exception_count; i++) {\n      ExceptionHandler handler = new ExceptionHandler();\n      handler.from = in.readUnsignedShort();\n      handler.to = in.readUnsignedShort();\n      handler.handler = in.readUnsignedShort();\n\n      int excclass = in.readUnsignedShort();\n      if (excclass != 0) {\n        handler.exceptionClass = pool.getPrimitiveConstant(excclass).getString();\n      }\n\n      lstHandlers.add(handler);\n    }\n\n    InstructionSequence seq = new FullInstructionSequence(instructions, new ExceptionTable(lstHandlers));\n\n    // initialize instructions\n    int i = seq.length() - 1;\n    seq.setPointer(i);\n\n    while (i >= 0) {\n      Instruction instr = seq.getInstr(i--);\n      if (instr.group != GROUP_GENERAL) {\n        instr.initInstruction(seq);\n      }\n      seq.addToPointer(-1);\n    }\n\n    return seq;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public String getDescriptor() {\n    return descriptor;\n  }\n\n  public int getBytecodeVersion() {\n    return bytecodeVersion;\n  }\n\n  public boolean containsCode() {\n    return codeLength >= 0;\n  }\n\n  public int getLocalVariables() {\n    return localVariables;\n  }\n\n  public InstructionSequence getInstructionSequence() {\n    return seq;\n  }\n\n  public StructLocalVariableTableAttribute getLocalVariableAttr() {\n    return getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TABLE);\n  }\n\n  @Override\n  protected Type getType() {\n    return MethodDescriptor.parseDescriptor(getDescriptor()).ret;\n  }\n\n  @Override\n  public String toString() {\n    return name;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/StructRecordComponent.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct;\n\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.Map;\n\n/*\n  record_component_info {\n    u2 name_index;\n    u2 descriptor_index;\n    u2 attributes_count;\n    attribute_info attributes[attributes_count];\n   }\n*/\npublic class StructRecordComponent extends StructField {\n  public static StructRecordComponent create(DataInputFullStream in, ConstantPool pool) throws IOException {\n    int nameIndex = in.readUnsignedShort();\n    int descriptorIndex = in.readUnsignedShort();\n\n    String name = ((PrimitiveConstant)pool.getConstant(nameIndex)).getString();\n    String descriptor = ((PrimitiveConstant)pool.getConstant(descriptorIndex)).getString();\n\n    Map<String, StructGeneralAttribute> attributes = readAttributes(in, pool);\n\n    return new StructRecordComponent(0, attributes, name, descriptor);\n  }\n\n  private StructRecordComponent(int flags, Map<String, StructGeneralAttribute> attributes, String name, String descriptor) {\n    super(flags, attributes, name, descriptor);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/StructTypePathEntry.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct;\n\n/**\n * An entry in the type path which indicates the path of a type annotation.\n */\npublic class StructTypePathEntry {\n  private final int typePathEntryKind;\n  private final int typeArgumentIndex;\n\n  public StructTypePathEntry(int typePathKind, int typeArgumentIndex) {\n    this.typePathEntryKind = typePathKind;\n    this.typeArgumentIndex = typeArgumentIndex;\n  }\n\n  /**\n   * @return The type argument index, indicating which nested type argument is annotated. The index starts at 0.\n   */\n  public int getTypeArgumentIndex() {\n    return typeArgumentIndex;\n  }\n\n  /**\n   * @return {@link Kind#id} of this type path entry.\n   */\n  public int getTypePathEntryKind() {\n    return typePathEntryKind;\n  }\n\n  /**\n   * The type_path_kind.\n   *\n   * @see <a href=\"https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html\">The JVM class File Format Spec</a> Table 4.7.20.2 A\n   */\n  public enum Kind {\n    /**\n     *  Type path entry is an array type contained in e.g. <code>@I String[] @G [] @H []</code>\n     */\n    ARRAY(0),\n\n    /**\n     *  Type path entry is a nested type contained in e.g. <code> Outer . @B Middle . @C Inner</code>\n     */\n    NESTED(1),\n\n    /**\n     *  Type path entry is a wildcard contained in e.g. <code>Map<@B ? extends String, String></code>\n     */\n    TYPE_WILDCARD(2),\n\n    /**\n     * Type path entry is a type argument of a parameterized type contained in e.g. <code>List<@B Comparable<@F Object>></code>\n     */\n    TYPE(3);\n\n    private final int id;\n\n    Kind(int id) {\n      this.id = id;\n    }\n\n    public int getId() {\n      return id;\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\n\npublic class StructAnnDefaultAttribute extends StructGeneralAttribute {\n\n  private Exprent defaultValue;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    defaultValue = StructAnnotationAttribute.parseAnnotationElement(data, pool);\n  }\n\n  public Exprent getDefaultValue() {\n    return defaultValue;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.*;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;\nimport org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.DataInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class StructAnnotationAttribute extends StructGeneralAttribute {\n  private List<AnnotationExprent> annotations;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    annotations = parseAnnotations(pool, data);\n  }\n\n  public static List<AnnotationExprent> parseAnnotations(ConstantPool pool, DataInputStream data) throws IOException {\n    int len = data.readUnsignedShort();\n    if (len > 0) {\n      List<AnnotationExprent> annotations = new ArrayList<>(len);\n      for (int i = 0; i < len; i++) {\n        annotations.add(parseAnnotation(data, pool));\n      }\n      return annotations;\n    }\n    else {\n      return Collections.emptyList();\n    }\n  }\n\n  public static @NotNull AnnotationExprent parseAnnotation(DataInputStream data, ConstantPool pool) throws IOException {\n    String className = pool.getPrimitiveConstant(data.readUnsignedShort()).getString();\n\n    List<String> names;\n    List<Exprent> values;\n    int len = data.readUnsignedShort();\n    if (len > 0) {\n      names = new ArrayList<>(len);\n      values = new ArrayList<>(len);\n      for (int i = 0; i < len; i++) {\n        names.add(pool.getPrimitiveConstant(data.readUnsignedShort()).getString());\n        values.add(parseAnnotationElement(data, pool));\n      }\n    }\n    else {\n      names = Collections.emptyList();\n      values = Collections.emptyList();\n    }\n\n    return new AnnotationExprent(new VarType(className).getValue(), names, values);\n  }\n\n  public static Exprent parseAnnotationElement(DataInputStream data, ConstantPool pool) throws IOException {\n    int tag = data.readUnsignedByte();\n\n    switch (tag) {\n      case 'e' -> { // enum constant\n        String className = pool.getPrimitiveConstant(data.readUnsignedShort()).getString();\n        String constName = pool.getPrimitiveConstant(data.readUnsignedShort()).getString();\n        FieldDescriptor descr = FieldDescriptor.parseDescriptor(className);\n        return new FieldExprent(constName, descr.type.getValue(), true, null, descr, null);\n      }\n      case 'c' -> { // class\n        String descriptor = pool.getPrimitiveConstant(data.readUnsignedShort()).getString();\n        VarType type = FieldDescriptor.parseDescriptor(descriptor).type;\n\n        String value = switch (type.getType()) {\n          case CodeConstants.TYPE_OBJECT -> type.getValue();\n          case CodeConstants.TYPE_BYTE -> byte.class.getName();\n          case CodeConstants.TYPE_CHAR -> char.class.getName();\n          case CodeConstants.TYPE_DOUBLE -> double.class.getName();\n          case CodeConstants.TYPE_FLOAT -> float.class.getName();\n          case CodeConstants.TYPE_INT -> int.class.getName();\n          case CodeConstants.TYPE_LONG -> long.class.getName();\n          case CodeConstants.TYPE_SHORT -> short.class.getName();\n          case CodeConstants.TYPE_BOOLEAN -> boolean.class.getName();\n          case CodeConstants.TYPE_VOID -> void.class.getName();\n          default -> throw new RuntimeException(\"invalid class type: \" + type.getType());\n        };\n        return new ConstExprent(VarType.VARTYPE_CLASS, value, null);\n      }\n      case '[' -> { // array\n        List<Exprent> elements = Collections.emptyList();\n        int len = data.readUnsignedShort();\n        if (len > 0) {\n          elements = new ArrayList<>(len);\n          for (int i = 0; i < len; i++) {\n            elements.add(parseAnnotationElement(data, pool));\n          }\n        }\n\n        VarType newType;\n        if (elements.isEmpty()) {\n          newType = new VarType(CodeConstants.TYPE_OBJECT, 1, \"java/lang/Object\");\n        }\n        else {\n          VarType elementType = elements.get(0).getExprType();\n          newType = new VarType(elementType.getType(), 1, elementType.getValue());\n        }\n\n        NewExprent newExpr = new NewExprent(newType, Collections.emptyList(), null);\n        newExpr.setDirectArrayInit(true);\n        newExpr.setLstArrayElements(elements);\n        return newExpr;\n      }\n      case '@' -> { // annotation\n        return parseAnnotation(data, pool);\n      }\n      default -> {\n        PrimitiveConstant cn = pool.getPrimitiveConstant(data.readUnsignedShort());\n        return switch (tag) {\n          case 'B' -> new ConstExprent(VarType.VARTYPE_BYTE, cn.value, null);\n          case 'C' -> new ConstExprent(VarType.VARTYPE_CHAR, cn.value, null);\n          case 'D' -> new ConstExprent(VarType.VARTYPE_DOUBLE, cn.value, null);\n          case 'F' -> new ConstExprent(VarType.VARTYPE_FLOAT, cn.value, null);\n          case 'I' -> new ConstExprent(VarType.VARTYPE_INT, cn.value, null);\n          case 'J' -> new ConstExprent(VarType.VARTYPE_LONG, cn.value, null);\n          case 'S' -> new ConstExprent(VarType.VARTYPE_SHORT, cn.value, null);\n          case 'Z' -> new ConstExprent(VarType.VARTYPE_BOOLEAN, cn.value, null);\n          case 's' -> new ConstExprent(VarType.VARTYPE_STRING, cn.value, null);\n          default -> throw new RuntimeException(\"invalid element type!\");\n        };\n      }\n    }\n  }\n\n  public List<AnnotationExprent> getAnnotations() {\n    return annotations;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class StructAnnotationParameterAttribute extends StructGeneralAttribute {\n\n  private List<List<AnnotationExprent>> paramAnnotations;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int len = data.readUnsignedByte();\n    if (len > 0) {\n      paramAnnotations = new ArrayList<>(len);\n      for (int i = 0; i < len; i++) {\n        List<AnnotationExprent> annotations = StructAnnotationAttribute.parseAnnotations(pool, data);\n        paramAnnotations.add(annotations);\n      }\n    }\n    else {\n      paramAnnotations = Collections.emptyList();\n    }\n  }\n\n  public List<List<AnnotationExprent>> getParamAnnotations() {\n    return paramAnnotations;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.consts.LinkConstant;\nimport org.jetbrains.java.decompiler.struct.consts.PooledConstant;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class StructBootstrapMethodsAttribute extends StructGeneralAttribute {\n\n  private final List<LinkConstant> methodRefs = new ArrayList<>();\n  private final List<List<PooledConstant>> methodArguments = new ArrayList<>();\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int method_number = data.readUnsignedShort();\n\n    for (int i = 0; i < method_number; ++i) {\n      int bootstrap_method_ref = data.readUnsignedShort();\n      int num_bootstrap_arguments = data.readUnsignedShort();\n\n      List<PooledConstant> list_arguments = new ArrayList<>();\n\n      for (int j = 0; j < num_bootstrap_arguments; ++j) {\n        int bootstrap_argument_ref = data.readUnsignedShort();\n\n        list_arguments.add(pool.getConstant(bootstrap_argument_ref));\n      }\n\n      methodRefs.add(pool.getLinkConstant(bootstrap_method_ref));\n      methodArguments.add(list_arguments);\n    }\n  }\n\n  public int getMethodsNumber() {\n    return methodRefs.size();\n  }\n\n  public LinkConstant getMethodReference(int index) {\n    return methodRefs.get(index);\n  }\n\n  public List<PooledConstant> getMethodArguments(int index) {\n    return methodArguments.get(index);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructCodeAttribute.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.StructMember;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.Map;\n\n/*\n  u2 max_stack;\n  u2 max_locals;\n  u4 code_length;\n  u1 code[];\n  u2 exception_table_length;\n  exception_table[] {\n     u2 start_pc;\n     u2 end_pc;\n     u2 handler_pc;\n     u2 catch_type;\n  };\n  u2 attributes_count;\n  attribute_info attributes[];\n*/\npublic class StructCodeAttribute extends StructGeneralAttribute {\n  public int localVariables = 0;\n  public int codeLength = 0;\n  public int codeFullLength = 0;\n  public Map<String, StructGeneralAttribute> codeAttributes;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    data.discard(2);\n    localVariables = data.readUnsignedShort();\n    codeLength = data.readInt();\n    data.discard(codeLength);\n    int excLength = data.readUnsignedShort();\n    data.discard(excLength * 8);\n    codeFullLength = codeLength + excLength * 8 + 2;\n    codeAttributes = StructMember.readAttributes(data, pool);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\n\npublic class StructConstantValueAttribute extends StructGeneralAttribute {\n\n  private int index;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    index = data.readUnsignedShort();\n  }\n\n  public int getIndex() {\n    return index;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.struct.consts.LinkConstant;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\n\npublic class StructEnclosingMethodAttribute extends StructGeneralAttribute {\n\n  private String className;\n  private String methodName;\n  private String methodDescriptor;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int classIndex = data.readUnsignedShort();\n    int methodIndex = data.readUnsignedShort();\n\n    className = pool.getPrimitiveConstant(classIndex).getString();\n    if (methodIndex != 0) {\n      LinkConstant lk = pool.getLinkConstant(methodIndex);\n      methodName = lk.elementName;\n      methodDescriptor = lk.descriptor;\n    }\n  }\n\n  public String getClassName() {\n    return className;\n  }\n\n  public String getMethodDescriptor() {\n    return methodDescriptor;\n  }\n\n  public String getMethodName() {\n    return methodName;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class StructExceptionsAttribute extends StructGeneralAttribute {\n\n  private List<Integer> throwsExceptions;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int len = data.readUnsignedShort();\n    if (len > 0) {\n      throwsExceptions = new ArrayList<>(len);\n      for (int i = 0; i < len; i++) {\n        throwsExceptions.add(data.readUnsignedShort());\n      }\n    }\n    else {\n      throwsExceptions = Collections.emptyList();\n    }\n  }\n\n  public String getExcClassname(int index, ConstantPool pool) {\n    return pool.getPrimitiveConstant(throwsExceptions.get(index)).getString();\n  }\n\n  public List<Integer> getThrowsExceptions() {\n    return throwsExceptions;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\n\n/*\n  attribute_info {\n    u2 attribute_name_index;\n    u4 attribute_length;\n    u1 info[attribute_length];\n  }\n*/\npublic class StructGeneralAttribute {\n  public static final Key<StructCodeAttribute> ATTRIBUTE_CODE = new Key<>(\"Code\");\n  public static final Key<StructInnerClassesAttribute> ATTRIBUTE_INNER_CLASSES = new Key<>(\"InnerClasses\");\n  public static final Key<StructGenericSignatureAttribute> ATTRIBUTE_SIGNATURE = new Key<>(\"Signature\");\n  public static final Key<StructAnnDefaultAttribute> ATTRIBUTE_ANNOTATION_DEFAULT = new Key<>(\"AnnotationDefault\");\n  public static final Key<StructExceptionsAttribute> ATTRIBUTE_EXCEPTIONS = new Key<>(\"Exceptions\");\n  public static final Key<StructEnclosingMethodAttribute> ATTRIBUTE_ENCLOSING_METHOD = new Key<>(\"EnclosingMethod\");\n  public static final Key<StructAnnotationAttribute> ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS = new Key<>(\"RuntimeVisibleAnnotations\");\n  public static final Key<StructAnnotationAttribute> ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS = new Key<>(\"RuntimeInvisibleAnnotations\");\n  public static final Key<?>[] ANNOTATION_ATTRIBUTES = {\n    ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS, ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS};\n  public static final Key<StructAnnotationParameterAttribute> ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = new Key<>(\"RuntimeVisibleParameterAnnotations\");\n  public static final Key<StructAnnotationParameterAttribute> ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = new Key<>(\"RuntimeInvisibleParameterAnnotations\");\n  public static final Key<?>[] PARAMETER_ANNOTATION_ATTRIBUTES = {\n    ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS};\n  public static final Key<StructTypeAnnotationAttribute> ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = new Key<>(\"RuntimeVisibleTypeAnnotations\");\n  public static final Key<StructTypeAnnotationAttribute> ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = new Key<>(\"RuntimeInvisibleTypeAnnotations\");\n  public static final Key<?>[] TYPE_ANNOTATION_ATTRIBUTES = {\n    ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS, ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS};\n  public static final Key<StructLocalVariableTableAttribute> ATTRIBUTE_LOCAL_VARIABLE_TABLE = new Key<>(\"LocalVariableTable\");\n  public static final Key<StructLocalVariableTypeTableAttribute> ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE = new Key<>(\"LocalVariableTypeTable\");\n  public static final Key<StructConstantValueAttribute> ATTRIBUTE_CONSTANT_VALUE = new Key<>(\"ConstantValue\");\n  public static final Key<StructBootstrapMethodsAttribute> ATTRIBUTE_BOOTSTRAP_METHODS = new Key<>(\"BootstrapMethods\");\n  public static final Key<StructGeneralAttribute> ATTRIBUTE_SYNTHETIC = new Key<>(\"Synthetic\");\n  public static final Key<StructGeneralAttribute> ATTRIBUTE_DEPRECATED = new Key<>(\"Deprecated\");\n  public static final Key<StructLineNumberTableAttribute> ATTRIBUTE_LINE_NUMBER_TABLE = new Key<>(\"LineNumberTable\");\n  public static final Key<StructMethodParametersAttribute> ATTRIBUTE_METHOD_PARAMETERS = new Key<>(\"MethodParameters\");\n  public static final Key<StructModuleAttribute> ATTRIBUTE_MODULE = new Key<>(\"Module\");\n  public static final Key<StructRecordAttribute> ATTRIBUTE_RECORD = new Key<>(\"Record\");\n  public static final Key<StructPermittedSubclassesAttribute> ATTRIBUTE_PERMITTED_SUBCLASSES = new Key<>(\"PermittedSubclasses\");\n\n  @SuppressWarnings(\"unused\")\n  public static class Key<T extends StructGeneralAttribute> {\n    public final String name;\n\n    public Key(String name) {\n      this.name = name;\n    }\n  }\n\n  public static StructGeneralAttribute createAttribute(String name) {\n    if (ATTRIBUTE_CODE.name.equals(name)) {\n      return new StructCodeAttribute();\n    }\n    else if (ATTRIBUTE_INNER_CLASSES.name.equals(name)) {\n      return new StructInnerClassesAttribute();\n    }\n    else if (ATTRIBUTE_CONSTANT_VALUE.name.equals(name)) {\n      return new StructConstantValueAttribute();\n    }\n    else if (ATTRIBUTE_SIGNATURE.name.equals(name)) {\n      return new StructGenericSignatureAttribute();\n    }\n    else if (ATTRIBUTE_ANNOTATION_DEFAULT.name.equals(name)) {\n      return new StructAnnDefaultAttribute();\n    }\n    else if (ATTRIBUTE_EXCEPTIONS.name.equals(name)) {\n      return new StructExceptionsAttribute();\n    }\n    else if (ATTRIBUTE_ENCLOSING_METHOD.name.equals(name)) {\n      return new StructEnclosingMethodAttribute();\n    }\n    else if (ATTRIBUTE_RUNTIME_VISIBLE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_ANNOTATIONS.name.equals(name)) {\n      return new StructAnnotationAttribute();\n    }\n    else if (ATTRIBUTE_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.name.equals(name)) {\n      return new StructAnnotationParameterAttribute();\n    }\n    else if (ATTRIBUTE_RUNTIME_VISIBLE_TYPE_ANNOTATIONS.name.equals(name) || ATTRIBUTE_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.name.equals(name)) {\n      return new StructTypeAnnotationAttribute();\n    }\n    else if (ATTRIBUTE_LOCAL_VARIABLE_TABLE.name.equals(name)) {\n      return new StructLocalVariableTableAttribute();\n    }\n    else if (ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE.name.equals(name)) {\n      return new StructLocalVariableTypeTableAttribute();\n    }\n    else if (ATTRIBUTE_BOOTSTRAP_METHODS.name.equals(name)) {\n      return new StructBootstrapMethodsAttribute();\n    }\n    else if (ATTRIBUTE_SYNTHETIC.name.equals(name) || ATTRIBUTE_DEPRECATED.name.equals(name)) {\n      return new StructGeneralAttribute();\n    }\n    else if (ATTRIBUTE_LINE_NUMBER_TABLE.name.equals(name)) {\n      return new StructLineNumberTableAttribute();\n    }\n    else if (ATTRIBUTE_METHOD_PARAMETERS.name.equals(name)) {\n      return new StructMethodParametersAttribute();\n    }\n    else if (ATTRIBUTE_MODULE.name.equals(name)) {\n      return new StructModuleAttribute();\n    }\n    else if (ATTRIBUTE_RECORD.name.equals(name)) {\n      return new StructRecordAttribute();\n    } else if (ATTRIBUTE_PERMITTED_SUBCLASSES.name.equals(name)) {\n      return new StructPermittedSubclassesAttribute();\n    }\n    else {\n      return null; // unsupported attribute\n    }\n  }\n\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException { }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\n\npublic class StructGenericSignatureAttribute extends StructGeneralAttribute {\n\n  private String signature;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int index = data.readUnsignedShort();\n    signature = pool.getPrimitiveConstant(index).getString();\n  }\n\n  public String getSignature() {\n    return signature;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class StructInnerClassesAttribute extends StructGeneralAttribute {\n  public static final class Entry {\n    public final int outerNameIdx;\n    public final int simpleNameIdx;\n    public final int accessFlags;\n    public final String innerName;\n    public final String enclosingName;\n    public final String simpleName;\n\n    private Entry(int outerNameIdx, int simpleNameIdx, int accessFlags, String innerName, String enclosingName, String simpleName) {\n      this.outerNameIdx = outerNameIdx;\n      this.simpleNameIdx = simpleNameIdx;\n      this.accessFlags = accessFlags;\n      this.innerName = innerName;\n      this.enclosingName = enclosingName;\n      this.simpleName = simpleName;\n    }\n  }\n\n  private List<Entry> entries;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int len = data.readUnsignedShort();\n    if (len > 0) {\n      entries = new ArrayList<>(len);\n\n      for (int i = 0; i < len; i++) {\n        int innerNameIdx = data.readUnsignedShort();\n        int outerNameIdx = data.readUnsignedShort();\n        int simpleNameIdx = data.readUnsignedShort();\n        int accessFlags = data.readUnsignedShort();\n\n        String innerName = pool.getPrimitiveConstant(innerNameIdx).getString();\n        String outerName = outerNameIdx != 0 ? pool.getPrimitiveConstant(outerNameIdx).getString() : null;\n        String simpleName = simpleNameIdx != 0 ? pool.getPrimitiveConstant(simpleNameIdx).getString() : null;\n\n        entries.add(new Entry(outerNameIdx, simpleNameIdx, accessFlags, innerName, outerName, simpleName));\n      }\n    }\n    else {\n      entries = Collections.emptyList();\n    }\n  }\n\n  public List<Entry> getEntries() {\n    return entries;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructLineNumberTableAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.io.IOException;\n\n/**\n * u2 line_number_table_length;\n * {  u2 start_pc;\n *    u2 line_number;\n * } line_number_table[line_number_table_length];\n *\n * Created by Egor on 05.10.2014.\n */\npublic class StructLineNumberTableAttribute extends StructGeneralAttribute {\n  private int[] myLineInfo = InterpreterUtil.EMPTY_INT_ARRAY;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int len = data.readUnsignedShort() * 2;\n    if (len > 0) {\n      myLineInfo = new int[len];\n      for (int i = 0; i < len; i += 2) {\n        myLineInfo[i] = data.readUnsignedShort();\n        myLineInfo[i + 1] = data.readUnsignedShort();\n      }\n    }\n    else if (myLineInfo.length > 0) {\n      myLineInfo = InterpreterUtil.EMPTY_INT_ARRAY;\n    }\n  }\n\n  public int findLineNumber(int pc) {\n    if (myLineInfo.length >= 2) {\n      for (int i = myLineInfo.length - 2; i >= 0; i -= 2) {\n        if (pc >= myLineInfo[i]) {\n          return myLineInfo[i + 1];\n        }\n      }\n    }\n    return -1;\n  }\n\n  public int[] getRawData() {\n    return myLineInfo;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/*\n  u2 local_variable_table_length;\n  local_variable {\n    u2 start_pc;\n    u2 length;\n    u2 name_index;\n    u2 descriptor_index;\n    u2 index;\n  }\n*/\npublic class StructLocalVariableTableAttribute extends StructGeneralAttribute {\n  private List<LocalVariable> localVariables = Collections.emptyList();\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int len = data.readUnsignedShort();\n    if (len > 0) {\n      localVariables = new ArrayList<>(len);\n\n      for (int i = 0; i < len; i++) {\n        int start_pc = data.readUnsignedShort();\n        int length = data.readUnsignedShort();\n        int nameIndex = data.readUnsignedShort();\n        int descriptorIndex = data.readUnsignedShort();\n        int varIndex = data.readUnsignedShort();\n        localVariables.add(new LocalVariable(start_pc,\n                                             length,\n                                             pool.getPrimitiveConstant(nameIndex).getString(),\n                                             pool.getPrimitiveConstant(descriptorIndex).getString(),\n                                             varIndex));\n      }\n    }\n    else {\n      localVariables = Collections.emptyList();\n    }\n  }\n\n  public void add(StructLocalVariableTableAttribute attr) {\n    localVariables.addAll(attr.localVariables);\n  }\n\n  public String getName(int index, int visibleOffset) {\n    return matchingVars(index, visibleOffset).map(v -> v.name).findFirst().orElse(null);\n  }\n\n  public String getDescriptor(int index, int visibleOffset) {\n    return matchingVars(index, visibleOffset).map(v -> v.descriptor).findFirst().orElse(null);\n  }\n\n  private Stream<LocalVariable> matchingVars(int index, int visibleOffset) {\n    return localVariables.stream().filter(v -> v.index == index && (visibleOffset >= v.start_pc && visibleOffset < v.start_pc + v.length));\n  }\n\n  public boolean containsName(String name) {\n    return localVariables.stream().anyMatch(v -> Objects.equals(v.name, name));\n  }\n\n  public Map<Integer, String> getMapParamNames() {\n    return localVariables.stream().filter(v -> v.start_pc == 0).collect(Collectors.toMap(v -> v.index, v -> v.name, (n1, n2) -> n2));\n  }\n\n  private static final class LocalVariable {\n    final int start_pc;\n    final int length;\n    final String name;\n    final String descriptor;\n    final int index;\n\n    private LocalVariable(int start_pc, int length, String name, String descriptor, int index) {\n      this.start_pc = start_pc;\n      this.length = length;\n      this.name = name;\n      this.descriptor = descriptor;\n      this.index = index;\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTypeTableAttribute.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\n\n/*\n  u2 local_variable_type_table_length;\n    {   u2 start_pc;\n        u2 length;\n        u2 name_index;\n        u2 signature_index;\n        u2 index;\n    } local_variable_type_table[local_variable_type_table_length];\n*/\npublic class StructLocalVariableTypeTableAttribute extends StructGeneralAttribute {\n  // store signature instead of descriptor\n  private final StructLocalVariableTableAttribute backingAttribute = new StructLocalVariableTableAttribute();\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    backingAttribute.initContent(data, pool);\n  }\n\n  public void add(StructLocalVariableTypeTableAttribute attr) {\n    backingAttribute.add(attr.backingAttribute);\n  }\n\n  public String getSignature(int index, int visibleOffset) {\n    return backingAttribute.getDescriptor(index, visibleOffset);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructMethodParametersAttribute.java",
    "content": "/*\n * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\n */\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/*\n  u1 parameters_count;\n  {   u2 name_index;\n      u2 access_flags;\n  } parameters[parameters_count];\n*/\npublic class StructMethodParametersAttribute extends StructGeneralAttribute {\n  private List<Entry> myEntries;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int len = data.readUnsignedByte();\n    List<Entry> entries;\n    if (len > 0) {\n      entries = new ArrayList<>(len);\n\n      for (int i = 0; i < len; i++) {\n        int nameIndex = data.readUnsignedShort();\n        String name = nameIndex != 0 ? pool.getPrimitiveConstant(nameIndex).getString() : null;\n        int access_flags = data.readUnsignedShort();\n        entries.add(new Entry(name, access_flags));\n      }\n    }\n    else {\n      entries = Collections.emptyList();\n    }\n    myEntries = Collections.unmodifiableList(entries);\n  }\n\n  public List<Entry> getEntries() {\n    return myEntries;\n  }\n\n  public static class Entry {\n    public final String myName;\n    public final int myAccessFlags;\n\n    public Entry(String name, int accessFlags) {\n      myName = name;\n      myAccessFlags = accessFlags;\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructModuleAttribute.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class StructModuleAttribute extends StructGeneralAttribute {\n  public String moduleName;\n  public int moduleFlags;\n  public String moduleVersion;\n\n  public List<RequiresEntry> requires;\n  public List<ExportsEntry> exports;\n  public List<OpensEntry> opens;\n  public List<String> uses;\n  public List<ProvidesEntry> provides;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int moduleNameIndex = data.readUnsignedShort();\n    this.moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();\n    this.moduleFlags = data.readUnsignedShort();\n\n    int moduleVersionIndex = data.readUnsignedShort();\n    if (moduleVersionIndex != 0) {\n      moduleVersion = pool.getPrimitiveConstant(moduleVersionIndex).getString();\n    }\n\n    this.requires = readRequires(data, pool);\n    this.exports = readExports(data, pool);\n    this.opens = readOpens(data, pool);\n    this.uses = readUses(data, pool);\n    this.provides = readProvides(data, pool);\n  }\n\n  public List<RequiresEntry> readRequires(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int requiresCount = data.readUnsignedShort();\n    if (requiresCount <= 0) return Collections.emptyList();\n\n    List<RequiresEntry> requires = new ArrayList<>(requiresCount);\n    for (int i = 0; i < requiresCount; i++) {\n      int moduleNameIndex = data.readUnsignedShort();\n      int requiresFlags = data.readUnsignedShort();\n      int versionIndex = data.readUnsignedShort();\n      String moduleName = pool.getPrimitiveConstant(moduleNameIndex).getString();\n      String version = versionIndex == 0 ? null : pool.getPrimitiveConstant(versionIndex).getString();\n      requires.add(new RequiresEntry(moduleName, requiresFlags, version));\n    }\n    return requires;\n  }\n\n  private static List<ExportsEntry> readExports(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int exportsCount = data.readUnsignedShort();\n    if (exportsCount <= 0) return Collections.emptyList();\n\n    List<ExportsEntry> exports = new ArrayList<>(exportsCount);\n    for (int i = 0; i < exportsCount; i++) {\n      int packageNameIndex = data.readUnsignedShort();\n      int exportsFlags = data.readUnsignedShort();\n      List<String> exportsToModules = readStringList(data, pool);\n      String packageName = pool.getPrimitiveConstant(packageNameIndex).getString();\n      exports.add(new ExportsEntry(packageName, exportsFlags, exportsToModules));\n    }\n    return exports;\n  }\n\n  private static List<OpensEntry> readOpens(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int opensCount = data.readUnsignedShort();\n    if (opensCount <= 0) return Collections.emptyList();\n\n    List<OpensEntry> opens = new ArrayList<>(opensCount);\n    for (int i = 0; i < opensCount; i++) {\n      int packageNameIndex = data.readUnsignedShort();\n      int opensFlags = data.readUnsignedShort();\n      List<String> opensToModules = readStringList(data, pool);\n      String packageName = pool.getPrimitiveConstant(packageNameIndex).getString();\n      opens.add(new OpensEntry(packageName, opensFlags, opensToModules));\n    }\n    return opens;\n  }\n\n  private static List<String> readUses(DataInputFullStream data, ConstantPool pool) throws IOException {\n    return readStringList(data, pool);\n  }\n\n  private static List<ProvidesEntry> readProvides(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int providesCount = data.readUnsignedShort();\n    if (providesCount <= 0) return Collections.emptyList();\n\n    List<ProvidesEntry> provides = new ArrayList<>(providesCount);\n    for (int i = 0; i < providesCount; i++) {\n      int interfaceNameIndex = data.readUnsignedShort();\n      String interfaceName = pool.getPrimitiveConstant(interfaceNameIndex).getString();\n      List<String> implementationNames = readStringList(data, pool);\n      provides.add(new ProvidesEntry(interfaceName, implementationNames));\n    }\n    return provides;\n  }\n\n  private static List<String> readStringList(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int count = data.readUnsignedShort();\n    if (count <= 0) {\n      return Collections.emptyList();\n    }\n    else {\n      List<String> strings = new ArrayList<>(count);\n      for (int i = 0; i < count; i++) {\n        int index = data.readUnsignedShort();\n        strings.add(pool.getPrimitiveConstant(index).getString());\n      }\n      return strings;\n    }\n  }\n\n  public static final class RequiresEntry {\n    public final String moduleName;\n    public final int flags;\n    public final String moduleVersion;\n\n    public RequiresEntry(String moduleName, int flags, String moduleVersion) {\n      this.moduleName = moduleName;\n      this.flags = flags;\n      this.moduleVersion = moduleVersion;\n    }\n  }\n\n  public static final class ExportsEntry {\n    public final String packageName;\n    public final int flags;\n    public final List<String> exportToModules;\n\n    public ExportsEntry(String packageName, int flags, List<String> exportToModules) {\n      this.packageName = packageName;\n      this.flags = flags;\n      this.exportToModules = exportToModules;\n    }\n  }\n\n  public static final class OpensEntry {\n    public final String packageName;\n    public final int flags;\n    public final List<String> opensToModules;\n\n    public OpensEntry(String packageName, int flags, List<String> exportToModules) {\n      this.packageName = packageName;\n      this.flags = flags;\n      this.opensToModules = exportToModules;\n    }\n  }\n\n  public static final class ProvidesEntry {\n    public final String interfaceName;\n    public final List<String> implementationNames;\n\n    public ProvidesEntry(String interfaceName, List<String> implementationNames) {\n      this.interfaceName = interfaceName;\n      this.implementationNames = implementationNames;\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructPermittedSubclassesAttribute.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\n\n/*\n  PermittedSubclasses_attribute {\n      u2 attribute_name_index;\n      u4 attribute_length;\n      u2 number_of_classes;\n      u2 classes[number_of_classes];\n  }\n */\npublic class StructPermittedSubclassesAttribute extends StructGeneralAttribute {\n  List<String> classes;\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int numberOfClasses = data.readUnsignedShort();\n    String[] classes = new String[numberOfClasses];\n    for (int i = 0; i < numberOfClasses; i++) {\n      classes[i] = pool.getPrimitiveConstant(data.readUnsignedShort()).getString();\n    }\n    this.classes = Arrays.asList(classes);\n  }\n\n  public List<String> getClasses() {\n    return classes;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructRecordAttribute.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.struct.StructRecordComponent;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n/*\n  Record_attribute {\n      u2 attribute_name_index;\n      u4 attribute_length;\n      u2 components_count;\n      record_component_info components[components_count];\n  }\n */\npublic class StructRecordAttribute extends StructGeneralAttribute {\n  List<StructRecordComponent> components;\n  \n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int componentCount = data.readUnsignedShort();\n    StructRecordComponent[] components = new StructRecordComponent[componentCount];\n    for (int i = 0; i < componentCount; i++) {\n      components[i] = StructRecordComponent.create(data, pool);\n    }\n    this.components = Arrays.asList(components);\n  }\n\n  public List<StructRecordComponent> getComponents() {\n    return Collections.unmodifiableList(components);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/attr/StructTypeAnnotationAttribute.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.attr;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.AnnotationExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TargetInfo;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TypeAnnotation;\nimport org.jetbrains.java.decompiler.struct.StructTypePathEntry;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.DataInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class StructTypeAnnotationAttribute extends StructGeneralAttribute {\n  private List<TypeAnnotation> annotations = Collections.emptyList();\n\n  @Override\n  public void initContent(DataInputFullStream data, ConstantPool pool) throws IOException {\n    int len = data.readUnsignedShort();\n    if (len > 0) {\n      annotations = new ArrayList<>(len);\n      for (int i = 0; i < len; i++) {\n        annotations.add(parse(data, pool));\n      }\n    }\n    else {\n      annotations = Collections.emptyList();\n    }\n  }\n\n  private static TypeAnnotation parse(DataInputStream data, ConstantPool pool) throws IOException {\n    int targetType = data.readUnsignedByte();\n\n    TargetInfo targetInfo = switch (targetType) {\n      case TypeAnnotation.CLASS_TYPE_PARAMETER, TypeAnnotation.METHOD_TYPE_PARAMETER ->\n        new TargetInfo.TypeParameterTarget(data.readUnsignedByte());\n      case TypeAnnotation.SUPER_TYPE_REFERENCE ->\n        new TargetInfo.SupertypeTarget(data.readUnsignedShort());\n      case TypeAnnotation.CLASS_TYPE_PARAMETER_BOUND, TypeAnnotation.METHOD_TYPE_PARAMETER_BOUND ->\n        new TargetInfo.TypeParameterBoundTarget(data.readUnsignedByte(), data.readUnsignedByte());\n      case TypeAnnotation.FIELD, TypeAnnotation.METHOD_RETURN_TYPE, TypeAnnotation.METHOD_RECEIVER ->\n        new TargetInfo.EmptyTarget();\n      case TypeAnnotation.METHOD_PARAMETER ->\n        new TargetInfo.FormalParameterTarget(data.readUnsignedByte());\n      case TypeAnnotation.THROWS_REFERENCE ->\n        new TargetInfo.ThrowsTarget(data.readUnsignedShort());\n      case TypeAnnotation.LOCAL_VARIABLE, TypeAnnotation.RESOURCE_VARIABLE -> {\n        int tableLength = data.readUnsignedShort();\n        TargetInfo.LocalvarTarget.Offsets[] offsets = new TargetInfo.LocalvarTarget.Offsets[tableLength];\n        for (int i = 0; i < tableLength; i++) {\n          offsets[i] = new TargetInfo.LocalvarTarget.Offsets(data.readUnsignedShort(), data.readUnsignedShort(), data.readUnsignedShort());\n        }\n        yield new TargetInfo.LocalvarTarget(offsets);\n      }\n      case TypeAnnotation.CATCH_CLAUSE ->\n        new TargetInfo.CatchTarget(data.readUnsignedShort());\n      case TypeAnnotation.EXPR_INSTANCEOF, TypeAnnotation.EXPR_NEW, TypeAnnotation.EXPR_CONSTRUCTOR_REF, TypeAnnotation.EXPR_METHOD_REF ->\n        new TargetInfo.OffsetTarget(data.readUnsignedShort());\n      case TypeAnnotation.TYPE_ARG_CAST, TypeAnnotation.TYPE_ARG_CONSTRUCTOR_CALL, TypeAnnotation.TYPE_ARG_METHOD_CALL,\n        TypeAnnotation.TYPE_ARG_CONSTRUCTOR_REF, TypeAnnotation.TYPE_ARG_METHOD_REF ->\n        new TargetInfo.TypeArgumentTarget(data.readUnsignedShort(), data.readUnsignedByte());\n      default -> throw new RuntimeException(\"unknown target type: \" + targetType);\n    };\n\n    int pathLength = data.readUnsignedByte();\n    List<StructTypePathEntry> paths = new ArrayList<>(pathLength);\n    for (int i = 0; i < pathLength; i++) {\n      paths.add(i, new StructTypePathEntry(data.readUnsignedByte(), data.readUnsignedByte()));\n    }\n\n    AnnotationExprent annotation = StructAnnotationAttribute.parseAnnotation(data, pool);\n\n    return new TypeAnnotation(targetType, targetInfo, paths, annotation);\n  }\n\n  public List<TypeAnnotation> getAnnotations() {\n    return annotations;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.consts;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.ClassFormatException;\nimport org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor;\nimport org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;\nimport org.jetbrains.java.decompiler.struct.gen.NewClassNameBuilder;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.DataInputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.BitSet;\nimport java.util.List;\n\n@SuppressWarnings(\"AssignmentToForLoopParameter\")\npublic class ConstantPool implements NewClassNameBuilder {\n  public static final int FIELD = 1;\n  public static final int METHOD = 2;\n\n  private final List<PooledConstant> pool;\n  private final PoolInterceptor interceptor;\n\n  public ConstantPool(DataInputStream in) throws IOException {\n    int size = in.readUnsignedShort();\n    pool = new ArrayList<>(size);\n    BitSet[] nextPass = {new BitSet(size), new BitSet(size), new BitSet(size)};\n\n    // first dummy constant\n    pool.add(null);\n\n    // first pass: read the elements\n    for (int i = 1; i < size; i++) {\n      byte tag = (byte)in.readUnsignedByte();\n\n      switch (tag) {\n        case CodeConstants.CONSTANT_Utf8 ->\n          pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Utf8, in.readUTF()));\n        case CodeConstants.CONSTANT_Integer ->\n          pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Integer, Integer.valueOf(in.readInt())));\n        case CodeConstants.CONSTANT_Float ->\n          pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Float, in.readFloat()));\n        case CodeConstants.CONSTANT_Long -> {\n          pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Long, in.readLong()));\n          pool.add(null);\n          i++;\n        }\n        case CodeConstants.CONSTANT_Double -> {\n          pool.add(new PrimitiveConstant(CodeConstants.CONSTANT_Double, in.readDouble()));\n          pool.add(null);\n          i++;\n        }\n        case CodeConstants.CONSTANT_Class, CodeConstants.CONSTANT_String, CodeConstants.CONSTANT_MethodType, CodeConstants.CONSTANT_Module, CodeConstants.CONSTANT_Package -> {\n          pool.add(new PrimitiveConstant(tag, in.readUnsignedShort()));\n          nextPass[0].set(i);\n        }\n        case CodeConstants.CONSTANT_NameAndType -> {\n          pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort()));\n          nextPass[0].set(i);\n        }\n        case CodeConstants.CONSTANT_Fieldref, CodeConstants.CONSTANT_Methodref, CodeConstants.CONSTANT_InterfaceMethodref, CodeConstants.CONSTANT_Dynamic, CodeConstants.CONSTANT_InvokeDynamic -> {\n          pool.add(new LinkConstant(tag, in.readUnsignedShort(), in.readUnsignedShort()));\n          nextPass[1].set(i);\n        }\n        case CodeConstants.CONSTANT_MethodHandle -> {\n          pool.add(new LinkConstant(tag, in.readUnsignedByte(), in.readUnsignedShort()));\n          nextPass[2].set(i);\n        }\n        default ->\n          // Fail-fast on unknown constant pool entry.\n          // We have no chance to process this class correctly.\n          throw new ClassFormatException(\n            String.format(\"Unsupported constant pool entry type %d at index #%d! \", Byte.toUnsignedInt(tag), i));\n      }\n    }\n\n    // resolving complex pool elements\n    for (BitSet pass : nextPass) {\n      int idx = 0;\n      while ((idx = pass.nextSetBit(idx + 1)) > 0) {\n        pool.get(idx).resolveConstant(this);\n      }\n    }\n\n    // get global constant pool interceptor instance, if any available\n    interceptor = DecompilerContext.getPoolInterceptor();\n  }\n\n  public static void skipPool(DataInputFullStream in) throws IOException {\n    int size = in.readUnsignedShort();\n\n    for (int i = 1; i < size; i++) {\n      switch (in.readUnsignedByte()) {\n        case CodeConstants.CONSTANT_Utf8 -> in.readUTF();\n        case CodeConstants.CONSTANT_Integer, CodeConstants.CONSTANT_Float, CodeConstants.CONSTANT_Fieldref, CodeConstants.CONSTANT_Methodref, CodeConstants.CONSTANT_InterfaceMethodref, CodeConstants.CONSTANT_NameAndType, CodeConstants.CONSTANT_Dynamic, CodeConstants.CONSTANT_InvokeDynamic ->\n          in.discard(4);\n        case CodeConstants.CONSTANT_Long, CodeConstants.CONSTANT_Double -> {\n          in.discard(8);\n          i++;\n        }\n        case CodeConstants.CONSTANT_Class, CodeConstants.CONSTANT_String, CodeConstants.CONSTANT_MethodType -> in.discard(2);\n        case CodeConstants.CONSTANT_MethodHandle -> in.discard(3);\n      }\n    }\n  }\n\n  public String[] getClassElement(int elementType, String className, int nameIndex, int descriptorIndex) {\n    String elementName = ((PrimitiveConstant)getConstant(nameIndex)).getString();\n    String descriptor = ((PrimitiveConstant)getConstant(descriptorIndex)).getString();\n\n    if (interceptor != null) {\n      String oldClassName = interceptor.getOldName(className);\n      if (oldClassName != null) {\n        className = oldClassName;\n      }\n\n      String newElement = interceptor.getName(className + ' ' + elementName + ' ' + descriptor);\n      if (newElement != null) {\n        elementName = newElement.split(\" \")[1];\n      }\n\n      String newDescriptor = buildNewDescriptor(elementType == FIELD, descriptor);\n      if (newDescriptor != null) {\n        descriptor = newDescriptor;\n      }\n    }\n\n    return new String[]{elementName, descriptor};\n  }\n\n  public PooledConstant getConstant(int index) {\n    return pool.get(index);\n  }\n\n  public PrimitiveConstant getPrimitiveConstant(int index) {\n    PrimitiveConstant cn = (PrimitiveConstant)getConstant(index);\n\n    if (cn != null && interceptor != null) {\n      if (cn.type == CodeConstants.CONSTANT_Class) {\n        String newName = buildNewClassname(cn.getString());\n        if (newName != null) {\n          cn = new PrimitiveConstant(CodeConstants.CONSTANT_Class, newName);\n        }\n      }\n    }\n\n    return cn;\n  }\n\n  public LinkConstant getLinkConstant(int index) {\n    LinkConstant ln = (LinkConstant)getConstant(index);\n\n    if (ln != null && interceptor != null &&\n        (ln.type == CodeConstants.CONSTANT_Fieldref ||\n         ln.type == CodeConstants.CONSTANT_Methodref ||\n         ln.type == CodeConstants.CONSTANT_InterfaceMethodref)) {\n      String newClassName = buildNewClassname(ln.className);\n      String newElement = interceptor.getName(ln.className + ' ' + ln.elementName + ' ' + ln.descriptor);\n      String newDescriptor = buildNewDescriptor(ln.type == CodeConstants.CONSTANT_Fieldref, ln.descriptor);\n      //TODO: Fix newElement being null caused by ln.classname being a leaf class instead of the class that declared the field/method.\n      //See the comments of IDEA-137253 for more information.\n      if (newClassName != null || newElement != null || newDescriptor != null) {\n        String className = newClassName == null ? ln.className : newClassName;\n        String elementName = newElement == null ? ln.elementName : newElement.split(\" \")[1];\n        String descriptor = newDescriptor == null ? ln.descriptor : newDescriptor;\n        ln = new LinkConstant(ln.type, className, elementName, descriptor);\n      }\n    }\n\n    return ln;\n  }\n\n  @Override\n  public String buildNewClassname(String className) {\n    VarType vt = new VarType(className, true);\n\n    String newName = interceptor.getName(vt.getValue());\n    if (newName != null) {\n      StringBuilder buffer = new StringBuilder();\n      if (vt.getArrayDim() > 0) {\n        buffer.append(\"[\".repeat(vt.getArrayDim())).append('L').append(newName).append(';');\n      }\n      else {\n        buffer.append(newName);\n      }\n      return buffer.toString();\n    }\n\n    return null;\n  }\n\n  private String buildNewDescriptor(boolean isField, String descriptor) {\n    if (isField) {\n      return FieldDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this);\n    }\n    else {\n      return MethodDescriptor.parseDescriptor(descriptor).buildNewDescriptor(this);\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.consts;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\n\npublic class LinkConstant extends PooledConstant {\n  public int index1, index2;\n  public String className;\n  public String elementName;\n  public String descriptor;\n\n  public LinkConstant(int type, String className, String elementName, String descriptor) {\n    super(type);\n    this.className = className;\n    this.elementName = elementName;\n    this.descriptor = descriptor;\n\n    initConstant();\n  }\n\n  public LinkConstant(int type, int index1, int index2) {\n    super(type);\n    this.index1 = index1;\n    this.index2 = index2;\n  }\n\n  private void initConstant() {\n    if (type == CodeConstants.CONSTANT_Methodref ||\n        type == CodeConstants.CONSTANT_InterfaceMethodref ||\n        type == CodeConstants.CONSTANT_InvokeDynamic ||\n        (type == CodeConstants.CONSTANT_MethodHandle && index1 != CodeConstants.CONSTANT_MethodHandle_REF_getField &&\n         index1 != CodeConstants.CONSTANT_MethodHandle_REF_putField)) {\n      int parenth = descriptor.indexOf(')');\n      if (descriptor.length() < 2 || parenth < 0 || descriptor.charAt(0) != '(') {\n        throw new IllegalArgumentException(\"Invalid descriptor: \" + descriptor +\n                                           \"; type = \" + type + \"; className = \" + className + \"; elementName = \" + elementName);\n      }\n    }\n  }\n\n  @Override\n  public void resolveConstant(ConstantPool pool) {\n    if (type == CodeConstants.CONSTANT_NameAndType) {\n      elementName = pool.getPrimitiveConstant(index1).getString();\n      descriptor = pool.getPrimitiveConstant(index2).getString();\n    }\n    else if (type == CodeConstants.CONSTANT_MethodHandle) {\n      LinkConstant ref_info = pool.getLinkConstant(index2);\n\n      className = ref_info.className;\n      elementName = ref_info.elementName;\n      descriptor = ref_info.descriptor;\n    }\n    else {\n      if (type != CodeConstants.CONSTANT_InvokeDynamic && type != CodeConstants.CONSTANT_Dynamic) {\n        className = pool.getPrimitiveConstant(index1).getString();\n      }\n\n      LinkConstant nameType = pool.getLinkConstant(index2);\n      elementName = nameType.elementName;\n      descriptor = nameType.descriptor;\n    }\n\n    initConstant();\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof LinkConstant cn)) return false;\n\n    return this.type == cn.type &&\n           this.elementName.equals(cn.elementName) &&\n           this.descriptor.equals(cn.descriptor) &&\n           (this.type != CodeConstants.CONSTANT_NameAndType || this.className.equals(cn.className));\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.consts;\n\npublic class PooledConstant {\n  public final int type;\n\n  public PooledConstant(int type) {\n    this.type = type;\n  }\n\n  public void resolveConstant(ConstantPool pool) { }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.consts;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\n\npublic class PrimitiveConstant extends PooledConstant {\n  public int index;\n  public Object value;\n  public boolean isArray;\n\n  public PrimitiveConstant(int type, Object value) {\n    super(type);\n    this.value = value;\n\n    initConstant();\n  }\n\n  public PrimitiveConstant(int type, int index) {\n    super(type);\n    this.index = index;\n  }\n\n  private void initConstant() {\n    if (type == CodeConstants.CONSTANT_Class) {\n      String className = getString();\n      isArray = (!className.isEmpty() && className.charAt(0) == '['); // empty string for a class name seems to be possible in some android files\n    }\n  }\n\n  public String getString() {\n    return (String)value;\n  }\n\n  @Override\n  public void resolveConstant(ConstantPool pool) {\n    if (type == CodeConstants.CONSTANT_Class || type == CodeConstants.CONSTANT_String || type == CodeConstants.CONSTANT_MethodType ||\n        type == CodeConstants.CONSTANT_Module || type == CodeConstants.CONSTANT_Package) {\n      value = pool.getPrimitiveConstant(index).getString();\n      initConstant();\n    }\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof PrimitiveConstant cn)) return false;\n\n    return this.type == cn.type &&\n           this.isArray == cn.isArray &&\n           this.value.equals(cn.value);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.gen;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.util.ListStack;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class DataPoint {\n  private final List<VarType> localVariables;\n  private final ListStack<VarType> stack;\n\n  public DataPoint() {\n    this(new ArrayList<>(), new ListStack<>());\n  }\n\n  private DataPoint(List<VarType> localVariables, ListStack<VarType> stack) {\n    this.localVariables = localVariables;\n    this.stack = stack;\n  }\n\n  public DataPoint copy() {\n    return new DataPoint(new ArrayList<>(localVariables), stack.copy());\n  }\n\n  public void setVariable(int index, VarType value) {\n    if (index >= localVariables.size()) {\n      for (int i = localVariables.size(); i <= index; i++) {\n        localVariables.add(new VarType(CodeConstants.TYPE_NOTINITIALIZED));\n      }\n    }\n\n    localVariables.set(index, value);\n  }\n\n  public VarType getVariable(int index) {\n    if (index < localVariables.size()) {\n      return localVariables.get(index);\n    }\n    else {\n      return new VarType(CodeConstants.TYPE_NOTINITIALIZED);\n    }\n  }\n\n  public static DataPoint getInitialDataPoint(StructMethod mt) {\n    DataPoint point = new DataPoint();\n\n    MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());\n\n    int k = 0;\n    if (!mt.hasModifier(CodeConstants.ACC_STATIC)) {\n      point.setVariable(k++, new VarType(CodeConstants.TYPE_OBJECT, 0, null));\n    }\n\n    for (int i = 0; i < md.params.length; i++) {\n      VarType var = md.params[i];\n\n      point.setVariable(k++, var);\n      if (var.getStackSize() == 2) {\n        point.setVariable(k++, new VarType(CodeConstants.TYPE_GROUP2EMPTY));\n      }\n    }\n\n    return point;\n  }\n\n  public ListStack<VarType> getStack() {\n    return stack;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.gen;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\n\npublic final class FieldDescriptor {\n  public static final FieldDescriptor SHORT_DESCRIPTOR = parseDescriptor(\"Ljava/lang/Short;\");\n  public static final FieldDescriptor INTEGER_DESCRIPTOR = parseDescriptor(\"Ljava/lang/Integer;\");\n  public static final FieldDescriptor LONG_DESCRIPTOR = parseDescriptor(\"Ljava/lang/Long;\");\n  public static final FieldDescriptor FLOAT_DESCRIPTOR = parseDescriptor(\"Ljava/lang/Float;\");\n  public static final FieldDescriptor DOUBLE_DESCRIPTOR = parseDescriptor(\"Ljava/lang/Double;\");\n\n  public final VarType type;\n  public final String descriptorString;\n\n  private FieldDescriptor(String descriptor) {\n    type = new VarType(descriptor);\n    descriptorString = descriptor;\n  }\n\n  public static FieldDescriptor parseDescriptor(String descriptor) {\n    return new FieldDescriptor(descriptor);\n  }\n\n  public String buildNewDescriptor(NewClassNameBuilder builder) {\n    if (type.getType() == CodeConstants.TYPE_OBJECT) {\n      String newClassName = builder.buildNewClassname(type.getValue());\n      if (newClassName != null) {\n        return new VarType(type.getType(), type.getArrayDim(), newClassName).toString();\n      }\n    }\n\n    return null;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof FieldDescriptor fd)) return false;\n\n    return type.equals(fd.type);\n  }\n\n  @Override\n  public int hashCode() {\n    return type.hashCode();\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.gen;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic final class MethodDescriptor {\n  public final VarType[] params;\n  public final VarType ret;\n\n  private MethodDescriptor(VarType[] params, VarType ret) {\n    this.params = params;\n    this.ret = ret;\n  }\n\n  public static MethodDescriptor parseDescriptor(String descriptor) {\n    int parenth = descriptor.lastIndexOf(')');\n    if (descriptor.length() < 2 || parenth < 0 || descriptor.charAt(0) != '(') {\n      throw new IllegalArgumentException(\"Invalid descriptor: \" + descriptor);\n    }\n\n    VarType[] params;\n\n    if (parenth > 1) {\n      String parameters = descriptor.substring(1, parenth);\n      List<String> lst = new ArrayList<>();\n\n      int indexFrom = -1, ind, len = parameters.length(), index = 0;\n      while (index < len) {\n        switch (parameters.charAt(index)) {\n          case '[' -> {\n            if (indexFrom < 0) {\n              indexFrom = index;\n            }\n          }\n          case 'L' -> {\n            ind = parameters.indexOf(\";\", index);\n            lst.add(parameters.substring(indexFrom < 0 ? index : indexFrom, ind + 1));\n            index = ind;\n            indexFrom = -1;\n          }\n          default -> {\n            lst.add(parameters.substring(indexFrom < 0 ? index : indexFrom, index + 1));\n            indexFrom = -1;\n          }\n        }\n        index++;\n      }\n\n      params = new VarType[lst.size()];\n      for (int i = 0; i < lst.size(); i++) {\n        params[i] = new VarType(lst.get(i));\n      }\n    }\n    else {\n      params = VarType.EMPTY_ARRAY;\n    }\n\n    VarType ret = new VarType(descriptor.substring(parenth + 1));\n\n    return new MethodDescriptor(params, ret);\n  }\n\n  public String buildNewDescriptor(NewClassNameBuilder builder) {\n    boolean updated = false;\n\n    VarType[] newParams;\n    if (params.length > 0) {\n      newParams = params.clone();\n      for (int i = 0; i < params.length; i++) {\n        VarType substitute = buildNewType(params[i], builder);\n        if (substitute != null) {\n          newParams[i] = substitute;\n          updated = true;\n        }\n      }\n    }\n    else {\n      newParams = VarType.EMPTY_ARRAY;\n    }\n\n    VarType newRet = ret;\n    VarType substitute = buildNewType(ret, builder);\n    if (substitute != null) {\n      newRet = substitute;\n      updated = true;\n    }\n\n    if (updated) {\n      StringBuilder res = new StringBuilder(\"(\");\n      for (VarType param : newParams) {\n        res.append(param);\n      }\n      res.append(\")\").append(newRet.toString());\n      return res.toString();\n    }\n\n    return null;\n  }\n\n  private static VarType buildNewType(VarType type, NewClassNameBuilder builder) {\n    if (type.getType() == CodeConstants.TYPE_OBJECT) {\n      String newClassName = builder.buildNewClassname(type.getValue());\n      if (newClassName != null) {\n        return new VarType(type.getType(), type.getArrayDim(), newClassName);\n      }\n    }\n    return null;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) return true;\n    if (!(o instanceof MethodDescriptor md)) return false;\n\n    return ret.equals(md.ret) && Arrays.equals(params, md.params);\n  }\n\n  @Override\n  public int hashCode() {\n    int result = ret.hashCode();\n    result = 31 * result + params.length;\n    return result;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/NewClassNameBuilder.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.gen;\n\npublic interface NewClassNameBuilder {\n  String buildNewClassname(String className);\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/Type.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.gen;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\n\nimport java.util.Arrays;\nimport java.util.List;\n\npublic interface Type {\n  int getType();\n\n  int getArrayDim();\n\n  String getValue();\n\n  /**\n   * Checks whether this Type can be annotated. Nested types can't be annotated when the right side of the currently considered types\n   * contains a reference to a static class.\n   */\n  default boolean isAnnotatable() {\n    List<String> nestedTypes = Arrays.asList(DecompilerContext.getImportCollector().getNestedName(getValue()).split(\"\\\\.\"));\n    if (nestedTypes.isEmpty()) return true;\n    String curPath = getValue().substring(0, getValue().lastIndexOf('/') + 1) + nestedTypes.get(0) + '$';\n    return ExprProcessor.canWriteNestedTypeAnnotation(curPath, nestedTypes.subList(1, nestedTypes.size()));\n  }\n}\n\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/VarType.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.gen;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\n\nimport java.util.Objects;\n\npublic class VarType implements Type {  // TODO: optimize switch\n\n  public static final VarType[] EMPTY_ARRAY = {};\n\n  public static final VarType VARTYPE_UNKNOWN = new VarType(CodeConstants.TYPE_UNKNOWN);\n  public static final VarType VARTYPE_INT = new VarType(CodeConstants.TYPE_INT);\n  public static final VarType VARTYPE_FLOAT = new VarType(CodeConstants.TYPE_FLOAT);\n  public static final VarType VARTYPE_LONG = new VarType(CodeConstants.TYPE_LONG);\n  public static final VarType VARTYPE_DOUBLE = new VarType(CodeConstants.TYPE_DOUBLE);\n  public static final VarType VARTYPE_BYTE = new VarType(CodeConstants.TYPE_BYTE);\n  public static final VarType VARTYPE_CHAR = new VarType(CodeConstants.TYPE_CHAR);\n  public static final VarType VARTYPE_SHORT = new VarType(CodeConstants.TYPE_SHORT);\n  public static final VarType VARTYPE_BOOLEAN = new VarType(CodeConstants.TYPE_BOOLEAN);\n  public static final VarType VARTYPE_BYTECHAR = new VarType(CodeConstants.TYPE_BYTECHAR);\n  public static final VarType VARTYPE_SHORTCHAR = new VarType(CodeConstants.TYPE_SHORTCHAR);\n\n  public static final VarType VARTYPE_NULL = new VarType(CodeConstants.TYPE_NULL, 0, null);\n  public static final VarType VARTYPE_STRING = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/String\");\n  public static final VarType VARTYPE_CLASS = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Class\");\n  public static final VarType VARTYPE_OBJECT = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Object\");\n  public static final VarType VARTYPE_INTEGER = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Integer\");\n  public static final VarType VARTYPE_CHARACTER = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Character\");\n  public static final VarType VARTYPE_BYTE_OBJ = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Byte\");\n  public static final VarType VARTYPE_SHORT_OBJ = new VarType(CodeConstants.TYPE_OBJECT, 0, \"java/lang/Short\");\n  public static final VarType VARTYPE_VOID = new VarType(CodeConstants.TYPE_VOID);\n\n  private final int type;\n  private final int arrayDim;\n  private final String value;\n  private final int typeFamily;\n  private final int stackSize;\n  private final boolean falseBoolean;\n\n  public VarType(int type) {\n    this(type, 0);\n  }\n\n  public VarType(int type, int arrayDim) {\n    this(type, arrayDim, getChar(type));\n  }\n\n  public VarType(int type, int arrayDim, String value) {\n    this(type, arrayDim, value, getFamily(type, arrayDim), getStackSize(type, arrayDim), false);\n  }\n\n  private VarType(int type, int arrayDim, String value, int typeFamily, int stackSize, boolean falseBoolean) {\n    this.type = type;\n    this.arrayDim = arrayDim;\n    this.value = value;\n    this.typeFamily = typeFamily;\n    this.stackSize = stackSize;\n    this.falseBoolean = falseBoolean;\n  }\n\n  public VarType(String signature) {\n    this(signature, false);\n  }\n\n  public VarType(String signature, boolean clType) {\n    int type = 0;\n    int arrayDim = 0;\n    String value = null;\n\n    loop:\n    for (int i = 0; i < signature.length(); i++) {\n      switch (signature.charAt(i)) {\n        case '[':\n          arrayDim++;\n          break;\n\n        case 'L':\n          if (signature.charAt(signature.length() - 1) == ';') {\n            type = CodeConstants.TYPE_OBJECT;\n            value = signature.substring(i + 1, signature.length() - 1);\n            break loop;\n          }\n\n        default:\n          value = signature.substring(i);\n          if ((clType && i == 0) || value.length() > 1) {\n            type = CodeConstants.TYPE_OBJECT;\n          }\n          else {\n            type = getType(value.charAt(0));\n          }\n          break loop;\n      }\n    }\n\n    this.type = type;\n    this.arrayDim = arrayDim;\n    this.value = value;\n    this.typeFamily = getFamily(type, arrayDim);\n    this.stackSize = getStackSize(type, arrayDim);\n    this.falseBoolean = false;\n  }\n\n  @Override\n  public int getType() {\n    return type;\n  }\n\n  @Override\n  public int getArrayDim() {\n    return arrayDim;\n  }\n\n  @Override\n  public String getValue() {\n    return value;\n  }\n\n  public int getTypeFamily() {\n    return typeFamily;\n  }\n\n  public int getStackSize() {\n    return stackSize;\n  }\n\n  private static String getChar(int type) {\n    return switch (type) {\n      case CodeConstants.TYPE_BYTE -> \"B\";\n      case CodeConstants.TYPE_CHAR -> \"C\";\n      case CodeConstants.TYPE_DOUBLE -> \"D\";\n      case CodeConstants.TYPE_FLOAT -> \"F\";\n      case CodeConstants.TYPE_INT -> \"I\";\n      case CodeConstants.TYPE_LONG -> \"J\";\n      case CodeConstants.TYPE_SHORT -> \"S\";\n      case CodeConstants.TYPE_BOOLEAN -> \"Z\";\n      case CodeConstants.TYPE_VOID -> \"V\";\n      case CodeConstants.TYPE_GROUP2EMPTY -> \"G\";\n      case CodeConstants.TYPE_NOTINITIALIZED -> \"N\";\n      case CodeConstants.TYPE_ADDRESS -> \"A\";\n      case CodeConstants.TYPE_BYTECHAR -> \"X\";\n      case CodeConstants.TYPE_SHORTCHAR -> \"Y\";\n      case CodeConstants.TYPE_UNKNOWN -> \"U\";\n      case CodeConstants.TYPE_NULL, CodeConstants.TYPE_OBJECT -> null;\n      default -> throw new RuntimeException(\"Invalid type\");\n    };\n  }\n\n  private static int getStackSize(int type, int arrayDim) {\n    if (arrayDim > 0) {\n      return 1;\n    }\n\n    return switch (type) {\n      case CodeConstants.TYPE_DOUBLE, CodeConstants.TYPE_LONG -> 2;\n      case CodeConstants.TYPE_VOID, CodeConstants.TYPE_GROUP2EMPTY -> 0;\n      default -> 1;\n    };\n  }\n\n  private static int getFamily(int type, int arrayDim) {\n    if (arrayDim > 0) {\n      return CodeConstants.TYPE_FAMILY_OBJECT;\n    }\n\n    return switch (type) {\n      case CodeConstants.TYPE_BYTE, CodeConstants.TYPE_BYTECHAR, CodeConstants.TYPE_SHORTCHAR, CodeConstants.TYPE_CHAR,\n        CodeConstants.TYPE_SHORT, CodeConstants.TYPE_INT -> CodeConstants.TYPE_FAMILY_INTEGER;\n      case CodeConstants.TYPE_DOUBLE -> CodeConstants.TYPE_FAMILY_DOUBLE;\n      case CodeConstants.TYPE_FLOAT -> CodeConstants.TYPE_FAMILY_FLOAT;\n      case CodeConstants.TYPE_LONG -> CodeConstants.TYPE_FAMILY_LONG;\n      case CodeConstants.TYPE_BOOLEAN -> CodeConstants.TYPE_FAMILY_BOOLEAN;\n      case CodeConstants.TYPE_NULL, CodeConstants.TYPE_OBJECT -> CodeConstants.TYPE_FAMILY_OBJECT;\n      default -> CodeConstants.TYPE_FAMILY_UNKNOWN;\n    };\n  }\n\n  public VarType decreaseArrayDim() {\n    if (getArrayDim() > 0) {\n      return new VarType(getType(), getArrayDim() - 1, getValue());\n    }\n    else {\n      //throw new RuntimeException(\"array dimension equals 0!\"); FIXME: investigate this case\n      return this;\n    }\n  }\n\n  public VarType resizeArrayDim(int newArrayDim) {\n    return new VarType(getType(), newArrayDim, getValue(), getTypeFamily(), getStackSize(), isFalseBoolean());\n  }\n\n  public VarType copy() {\n    return copy(false);\n  }\n\n  public VarType copy(boolean forceFalseBoolean) {\n    return new VarType(getType(), getArrayDim(), getValue(), getTypeFamily(), getStackSize(), isFalseBoolean() || forceFalseBoolean);\n  }\n\n  public boolean isFalseBoolean() {\n    return falseBoolean;\n  }\n\n  public boolean isSuperset(VarType val) {\n    return this.equals(val) || this.isStrictSuperset(val);\n  }\n\n  public boolean isStrictSuperset(VarType val) {\n    int valType = val.getType();\n\n    if (valType == CodeConstants.TYPE_UNKNOWN && getType() != CodeConstants.TYPE_UNKNOWN) {\n      return true;\n    }\n\n    if (val.getArrayDim() > 0) {\n      return this.equals(VARTYPE_OBJECT);\n    }\n    else if (getArrayDim() > 0) {\n      return (valType == CodeConstants.TYPE_NULL);\n    }\n\n    boolean res = false;\n\n    switch (getType()) {\n      case CodeConstants.TYPE_INT:\n        res = (valType == CodeConstants.TYPE_SHORT || valType == CodeConstants.TYPE_CHAR);\n      case CodeConstants.TYPE_SHORT:\n        res |= (valType == CodeConstants.TYPE_BYTE);\n      case CodeConstants.TYPE_CHAR:\n        res |= (valType == CodeConstants.TYPE_SHORTCHAR);\n      case CodeConstants.TYPE_BYTE:\n      case CodeConstants.TYPE_SHORTCHAR:\n        res |= (valType == CodeConstants.TYPE_BYTECHAR);\n      case CodeConstants.TYPE_BYTECHAR:\n        res |= (valType == CodeConstants.TYPE_BOOLEAN);\n        break;\n\n      case CodeConstants.TYPE_OBJECT:\n        if (valType == CodeConstants.TYPE_NULL) {\n          return true;\n        }\n        else if (this.equals(VARTYPE_OBJECT)) {\n          return valType == CodeConstants.TYPE_OBJECT && !val.equals(VARTYPE_OBJECT);\n        }\n    }\n\n    return res;\n  }\n\n  @Override\n  public boolean equals(Object o) {\n    if (o == this) {\n      return true;\n    }\n\n    if (!(o instanceof VarType vt)) {\n      return false;\n    }\n\n    return getType() == vt.getType() && getArrayDim() == vt.getArrayDim() && Objects.equals(getValue(), vt.getValue());\n  }\n\n  @Override\n  public String toString() {\n    StringBuilder res = new StringBuilder();\n    for (int i = 0; i < getArrayDim(); i++) {\n      res.append('[');\n    }\n    if (getType() == CodeConstants.TYPE_OBJECT) {\n      res.append('L').append(getValue()).append(';');\n    }\n    else {\n      res.append(getValue());\n    }\n    return res.toString();\n  }\n\n  // type1 and type2 must not be null\n  public static VarType getCommonMinType(VarType type1, VarType type2) {\n    if (type1.getType() == CodeConstants.TYPE_BOOLEAN && type2.getType() == CodeConstants.TYPE_BOOLEAN) { // special case booleans\n      return type1.isFalseBoolean() ? type2 : type1;\n    }\n\n    if (type1.isSuperset(type2)) {\n      return type2;\n    }\n    else if (type2.isSuperset(type1)) {\n      return type1;\n    }\n    else if (type1.getTypeFamily() == type2.getTypeFamily()) {\n      switch (type1.getTypeFamily()) {\n        case CodeConstants.TYPE_FAMILY_INTEGER -> {\n          if ((type1.getType() == CodeConstants.TYPE_CHAR && type2.getType() == CodeConstants.TYPE_SHORT)\n              || (type1.getType() == CodeConstants.TYPE_SHORT && type2.getType() == CodeConstants.TYPE_CHAR)) {\n            return VARTYPE_SHORTCHAR;\n          }\n          else {\n            return VARTYPE_BYTECHAR;\n          }\n        }\n        case CodeConstants.TYPE_FAMILY_OBJECT -> {\n          return VARTYPE_NULL;\n        }\n      }\n    }\n\n    return null;\n  }\n\n  // type1 and type2 must not be null\n  public static VarType getCommonSupertype(VarType type1, VarType type2) {\n    if (type1.getType() == CodeConstants.TYPE_BOOLEAN && type2.getType() == CodeConstants.TYPE_BOOLEAN) { // special case booleans\n      return type1.isFalseBoolean() ? type1 : type2;\n    }\n\n    if (type1.isSuperset(type2)) {\n      return type1;\n    }\n    else if (type2.isSuperset(type1)) {\n      return type2;\n    }\n    else if (type1.getTypeFamily() == type2.getTypeFamily()) {\n      switch (type1.getTypeFamily()) {\n        case CodeConstants.TYPE_FAMILY_INTEGER -> {\n          if ((type1.getType() == CodeConstants.TYPE_SHORTCHAR && type2.getType() == CodeConstants.TYPE_BYTE)\n              || (type1.getType() == CodeConstants.TYPE_BYTE && type2.getType() == CodeConstants.TYPE_SHORTCHAR)) {\n            return VARTYPE_SHORT;\n          }\n          else {\n            return VARTYPE_INT;\n          }\n        }\n        case CodeConstants.TYPE_FAMILY_OBJECT -> {\n          return VARTYPE_OBJECT;\n        }\n      }\n    }\n\n    return null;\n  }\n\n  public static VarType getMinTypeInFamily(int family) {\n    return switch (family) {\n      case CodeConstants.TYPE_FAMILY_BOOLEAN -> VARTYPE_BOOLEAN;\n      case CodeConstants.TYPE_FAMILY_INTEGER -> VARTYPE_BYTECHAR;\n      case CodeConstants.TYPE_FAMILY_OBJECT -> VARTYPE_NULL;\n      case CodeConstants.TYPE_FAMILY_FLOAT -> VARTYPE_FLOAT;\n      case CodeConstants.TYPE_FAMILY_LONG -> VARTYPE_LONG;\n      case CodeConstants.TYPE_FAMILY_DOUBLE -> VARTYPE_DOUBLE;\n      case CodeConstants.TYPE_FAMILY_UNKNOWN -> VARTYPE_UNKNOWN;\n      default -> throw new IllegalArgumentException(\"Invalid type family: \" + family);\n    };\n  }\n\n  public static int getType(char c) {\n    return switch (c) {\n      case 'B' -> CodeConstants.TYPE_BYTE;\n      case 'C' -> CodeConstants.TYPE_CHAR;\n      case 'D' -> CodeConstants.TYPE_DOUBLE;\n      case 'F' -> CodeConstants.TYPE_FLOAT;\n      case 'I' -> CodeConstants.TYPE_INT;\n      case 'J' -> CodeConstants.TYPE_LONG;\n      case 'S' -> CodeConstants.TYPE_SHORT;\n      case 'Z' -> CodeConstants.TYPE_BOOLEAN;\n      case 'V' -> CodeConstants.TYPE_VOID;\n      case 'G' -> CodeConstants.TYPE_GROUP2EMPTY;\n      case 'N' -> CodeConstants.TYPE_NOTINITIALIZED;\n      case 'A' -> CodeConstants.TYPE_ADDRESS;\n      case 'X' -> CodeConstants.TYPE_BYTECHAR;\n      case 'Y' -> CodeConstants.TYPE_SHORTCHAR;\n      case 'U' -> CodeConstants.TYPE_UNKNOWN;\n      default -> throw new IllegalArgumentException(\"Invalid type: \" + c);\n    };\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.gen.generics;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class GenericClassDescriptor {\n\n  public GenericType superclass;\n\n  public final List<GenericType> superinterfaces = new ArrayList<>();\n\n  public final List<String> fparameters = new ArrayList<>();\n\n  public final List<List<GenericType>> fbounds = new ArrayList<>();\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.gen.generics;\n\npublic class GenericFieldDescriptor {\n  public final GenericType type;\n\n  public GenericFieldDescriptor(GenericType type) {\n    this.type = type;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.gen.generics;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\nimport org.jetbrains.java.decompiler.modules.decompiler.typeann.TypeAnnotationWriteHelper;\nimport org.jetbrains.java.decompiler.struct.StructTypePathEntry;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic final class GenericMain {\n\n  private static final String[] typeNames = {\n    \"byte\",\n    \"char\",\n    \"double\",\n    \"float\",\n    \"int\",\n    \"long\",\n    \"short\",\n    \"boolean\",\n  };\n\n  public static GenericClassDescriptor parseClassSignature(String signature) {\n    String original = signature;\n    try {\n      GenericClassDescriptor descriptor = new GenericClassDescriptor();\n\n      signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds);\n\n      String superCl = GenericType.getNextType(signature);\n      descriptor.superclass = new GenericType(superCl);\n\n      signature = signature.substring(superCl.length());\n      while (signature.length() > 0) {\n        String superIf = GenericType.getNextType(signature);\n        descriptor.superinterfaces.add(new GenericType(superIf));\n        signature = signature.substring(superIf.length());\n      }\n\n      return descriptor;\n    }\n    catch (RuntimeException e) {\n      DecompilerContext.getLogger().writeMessage(\"Invalid signature: \" + original, IFernflowerLogger.Severity.WARN);\n      return null;\n    }\n  }\n\n  public static GenericFieldDescriptor parseFieldSignature(String signature) {\n    try {\n      return new GenericFieldDescriptor(new GenericType(signature));\n    }\n    catch (RuntimeException e) {\n      DecompilerContext.getLogger().writeMessage(\"Invalid signature: \" + signature, IFernflowerLogger.Severity.WARN);\n      return null;\n    }\n  }\n\n  public static GenericMethodDescriptor parseMethodSignature(String signature) {\n    String original = signature;\n    try {\n      List<String> typeParameters = new ArrayList<>();\n      List<List<GenericType>> typeParameterBounds = new ArrayList<>();\n      signature = parseFormalParameters(signature, typeParameters, typeParameterBounds);\n\n      int to = signature.indexOf(\")\");\n      String parameters = signature.substring(1, to);\n      signature = signature.substring(to + 1);\n\n      List<GenericType> parameterTypes = new ArrayList<>();\n      while (parameters.length() > 0) {\n        String par = GenericType.getNextType(parameters);\n        parameterTypes.add(new GenericType(par));\n        parameters = parameters.substring(par.length());\n      }\n\n      String ret = GenericType.getNextType(signature);\n      GenericType returnType = new GenericType(ret);\n      signature = signature.substring(ret.length());\n\n      List<GenericType> exceptionTypes = new ArrayList<>();\n      if (signature.length() > 0) {\n        String[] exceptions = signature.split(\"\\\\^\");\n        for (int i = 1; i < exceptions.length; i++) {\n          exceptionTypes.add(new GenericType(exceptions[i]));\n        }\n      }\n\n      return new GenericMethodDescriptor(typeParameters, typeParameterBounds, parameterTypes, returnType, exceptionTypes);\n    }\n    catch (RuntimeException e) {\n      DecompilerContext.getLogger().writeMessage(\"Invalid signature: \" + original, IFernflowerLogger.Severity.WARN);\n      return null;\n    }\n  }\n\n  private static String parseFormalParameters(String signature, List<? super String> parameters, List<? super List<GenericType>> bounds) {\n    if (signature.charAt(0) != '<') {\n      return signature;\n    }\n\n    int counter = 1;\n    int index = 1;\n\n    loop:\n    while (index < signature.length()) {\n      switch (signature.charAt(index)) {\n        case '<' -> counter++;\n        case '>' -> {\n          counter--;\n          if (counter == 0) {\n            break loop;\n          }\n        }\n      }\n\n      index++;\n    }\n\n    String value = signature.substring(1, index);\n    signature = signature.substring(index + 1);\n\n    while (value.length() > 0) {\n      int to = value.indexOf(\":\");\n\n      String param = value.substring(0, to);\n      value = value.substring(to + 1);\n\n      List<GenericType> lstBounds = new ArrayList<>();\n\n      while (true) {\n        if (value.charAt(0) == ':') {\n          // empty superclass, skip\n          value = value.substring(1);\n        }\n\n        String bound = GenericType.getNextType(value);\n        lstBounds.add(new GenericType(bound));\n        value = value.substring(bound.length());\n\n\n        if (value.length() == 0 || value.charAt(0) != ':') {\n          break;\n        }\n        else {\n          value = value.substring(1);\n        }\n      }\n\n      parameters.add(param);\n      bounds.add(lstBounds);\n    }\n\n    return signature;\n  }\n\n  public static String getGenericCastTypeName(GenericType type, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    List<TypeAnnotationWriteHelper> arrayTypeAnnWriteHelpers = ExprProcessor.arrayPath(type, typeAnnWriteHelpers);\n    List<TypeAnnotationWriteHelper> nonArrayTypeAnnWriteHelpers = ExprProcessor.nonArrayPath(type, typeAnnWriteHelpers);\n    StringBuilder sb = new StringBuilder(getTypeName(type, nonArrayTypeAnnWriteHelpers));\n    ExprProcessor.writeArray(sb, type.getArrayDim(), arrayTypeAnnWriteHelpers);\n    return sb.toString();\n  }\n\n  private static String getTypeName(GenericType type, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    int tp = type.getType();\n    if (tp <= CodeConstants.TYPE_BOOLEAN) {\n      return typeNames[tp];\n    }\n    else if (tp == CodeConstants.TYPE_VOID) {\n      return \"void\";\n    }\n    else if (tp == CodeConstants.TYPE_GENVAR) {\n      StringBuilder sb = new StringBuilder();\n      ExprProcessor.writeTypeAnnotationBeforeType(type, sb, typeAnnWriteHelpers);\n      sb.append(type.getValue());\n      return sb.toString();\n    }\n    else if (tp == CodeConstants.TYPE_OBJECT) {\n      StringBuilder sb = new StringBuilder();\n      appendClassName(type, sb, typeAnnWriteHelpers);\n      return sb.toString();\n    }\n\n    throw new RuntimeException(\"Invalid type: \" + type);\n  }\n\n  private static void appendClassName(GenericType type, StringBuilder sb, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {\n    List<GenericType> enclosingTypes = type.getEnclosingClasses();\n    typeAnnWriteHelpers = ExprProcessor.writeTypeAnnotationBeforeType(type, sb, typeAnnWriteHelpers);\n    if (enclosingTypes.isEmpty()) {\n      List<String> nestedTypes = Arrays.asList(\n        DecompilerContext.getImportCollector().getNestedName(type.getValue().replace('/', '.')).split(\"\\\\.\")\n      );\n      ExprProcessor.writeNestedClass(sb, type, nestedTypes, typeAnnWriteHelpers);\n      ExprProcessor.popNestedTypeAnnotation(typeAnnWriteHelpers);\n    }\n    else {\n      for (GenericType tp : enclosingTypes) {\n        List<String> nestedTypes = Arrays.asList(\n          DecompilerContext.getImportCollector().getNestedName(tp.getValue().replace('/', '.')).split(\"\\\\.\")\n        );\n        typeAnnWriteHelpers = ExprProcessor.writeNestedClass(sb, type, nestedTypes, typeAnnWriteHelpers);\n        typeAnnWriteHelpers = appendTypeArguments(tp, sb, typeAnnWriteHelpers);\n        ExprProcessor.popNestedTypeAnnotation(typeAnnWriteHelpers);\n        sb.append('.');\n      }\n      typeAnnWriteHelpers = ExprProcessor.writeNestedTypeAnnotations(sb, typeAnnWriteHelpers);\n      ExprProcessor.popNestedTypeAnnotation(typeAnnWriteHelpers);\n      sb.append(type.getValue());\n    }\n    appendTypeArguments(type, sb, typeAnnWriteHelpers);\n  }\n\n  private static List<TypeAnnotationWriteHelper> appendTypeArguments(\n    GenericType type,\n    StringBuilder sb,\n    List<TypeAnnotationWriteHelper> typeAnnWriteHelpers\n  ) {\n    if (!type.getArguments().isEmpty()) {\n      sb.append('<');\n\n      for (int i = 0; i < type.getArguments().size(); i++) {\n        if (i > 0) {\n          sb.append(\", \");\n        }\n\n        GenericType genPar = type.getArguments().get(i);\n        int wildcard = type.getWildcards().get(i);\n\n        // only take type paths that are in the generic\n        List<TypeAnnotationWriteHelper> locTypeAnnWriteHelpers = getGenericTypeAnnotations(i, typeAnnWriteHelpers);\n        typeAnnWriteHelpers.removeAll(locTypeAnnWriteHelpers);\n        locTypeAnnWriteHelpers = writeTypeAnnotationBeforeWildCard(sb, genPar, wildcard, locTypeAnnWriteHelpers);\n        switch (wildcard) {\n          case GenericType.WILDCARD_UNBOUND -> sb.append('?');\n          case GenericType.WILDCARD_EXTENDS -> sb.append(\"? extends \");\n          case GenericType.WILDCARD_SUPER -> sb.append(\"? super \");\n        }\n        locTypeAnnWriteHelpers = writeTypeAnnotationAfterWildCard(sb, genPar, locTypeAnnWriteHelpers);\n        if (genPar != null) {\n          sb.append(getGenericCastTypeName(genPar, locTypeAnnWriteHelpers));\n        }\n      }\n\n      sb.append(\">\");\n    }\n    return typeAnnWriteHelpers;\n  }\n\n  private static List<TypeAnnotationWriteHelper> getGenericTypeAnnotations(\n    int argIndex,\n    List<TypeAnnotationWriteHelper> typeAnnWriteHelpers\n  ) {\n    return typeAnnWriteHelpers.stream().filter(typeAnnWriteHelper -> {\n      StructTypePathEntry entry = typeAnnWriteHelper.getPaths().peek();\n      boolean inGeneric = entry != null && entry.getTypeArgumentIndex() == argIndex\n                          && entry.getTypePathEntryKind() == StructTypePathEntry.Kind.TYPE.getId();\n      if (inGeneric) typeAnnWriteHelper.getPaths().pop();\n      return inGeneric;\n    }).collect(Collectors.toList());\n  }\n\n  private static List<TypeAnnotationWriteHelper> writeTypeAnnotationBeforeWildCard(\n    StringBuilder sb,\n    GenericType type,\n    int wildcard,\n    List<TypeAnnotationWriteHelper> typeAnnWriteHelpers\n  ) {\n    return typeAnnWriteHelpers.stream().filter(typeAnnWriteHelper -> {\n      StructTypePathEntry path = typeAnnWriteHelper.getPaths().peek();\n      if (wildcard != GenericType.WILDCARD_NO && path == null) {\n        typeAnnWriteHelper.writeTo(sb);\n        return false;\n      }\n      if (type.getArrayDim() == typeAnnWriteHelper.getPaths().size() && type.getArrayDim() == typeAnnWriteHelper.arrayPathCount()) {\n        typeAnnWriteHelper.writeTo(sb);\n        return false;\n      }\n      return true;\n    }).collect(Collectors.toList());\n  }\n\n  private static List<TypeAnnotationWriteHelper> writeTypeAnnotationAfterWildCard(\n    StringBuilder sb,\n    GenericType type,\n    List<TypeAnnotationWriteHelper> typeAnnWriteHelpers\n  ) {\n    typeAnnWriteHelpers.forEach(typeAnnWriteHelper -> { // remove all wild card path entries\n      StructTypePathEntry path = typeAnnWriteHelper.getPaths().peek();\n      boolean isWildCard = path != null && path.getTypePathEntryKind() == StructTypePathEntry.Kind.TYPE_WILDCARD.getId();\n      if (isWildCard) typeAnnWriteHelper.getPaths().pop();\n    });\n    return typeAnnWriteHelpers.stream().filter(typeAnnWriteHelper -> {\n      StructTypePathEntry path = typeAnnWriteHelper.getPaths().peek();\n      if (type.getArrayDim() == 0 && path == null) {\n        typeAnnWriteHelper.writeTo(sb);\n        return false;\n      }\n      if (type.getArrayDim() == typeAnnWriteHelper.getPaths().size() && type.getArrayDim() == typeAnnWriteHelper.arrayPathCount()) {\n        typeAnnWriteHelper.writeTo(sb);\n        return false;\n      }\n      return true;\n    }).collect(Collectors.toList());\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.gen.generics;\n\nimport java.util.Collections;\nimport java.util.List;\n\npublic class GenericMethodDescriptor {\n  public final List<String> typeParameters;\n  public final List<List<GenericType>> typeParameterBounds;\n  public final List<GenericType> parameterTypes;\n  public final GenericType returnType;\n  public final List<GenericType> exceptionTypes;\n\n  public GenericMethodDescriptor(List<String> typeParameters,\n                                 List<List<GenericType>> typeParameterBounds,\n                                 List<GenericType> parameterTypes,\n                                 GenericType returnType,\n                                 List<GenericType> exceptionTypes) {\n    this.typeParameters = substitute(typeParameters);\n    this.typeParameterBounds = substitute(typeParameterBounds);\n    this.parameterTypes = substitute(parameterTypes);\n    this.returnType = returnType;\n    this.exceptionTypes = substitute(exceptionTypes);\n  }\n\n  private static <T> List<T> substitute(List<T> list) {\n    return list.isEmpty() ? Collections.emptyList() : list;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.struct.gen.generics;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.struct.gen.Type;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class GenericType implements Type {\n\n  public static final int WILDCARD_EXTENDS = 1;\n  public static final int WILDCARD_SUPER = 2;\n  public static final int WILDCARD_UNBOUND = 3;\n  public static final int WILDCARD_NO = 4;\n\n  private final int type;\n  private final int arrayDim;\n  private final String value;\n\n  private final List<GenericType> enclosingClasses = new ArrayList<>();\n  private final List<GenericType> arguments = new ArrayList<>();\n  private final List<Integer> wildcards = new ArrayList<>();\n\n  public GenericType(int type, int arrayDim, String value) {\n    this.type = type;\n    this.arrayDim = arrayDim;\n    this.value = value;\n  }\n\n  private GenericType(GenericType other, int arrayDim) {\n    this(other.getType(), arrayDim, other.getValue());\n    enclosingClasses.addAll(other.enclosingClasses);\n    arguments.addAll(other.arguments);\n    wildcards.addAll(other.wildcards);\n  }\n\n  public GenericType(String signature) {\n    int type = 0;\n    int arrayDim = 0;\n    String value = null;\n\n    int index = 0;\n    loop:\n    while (index < signature.length()) {\n      switch (signature.charAt(index)) {\n        case '[':\n          arrayDim++;\n          break;\n\n        case 'T':\n          type = CodeConstants.TYPE_GENVAR;\n          value = signature.substring(index + 1, signature.length() - 1);\n          break loop;\n\n        case 'L':\n          type = CodeConstants.TYPE_OBJECT;\n          signature = signature.substring(index + 1, signature.length() - 1);\n\n          while (true) {\n            String cl = getNextClassSignature(signature);\n\n            String name = cl;\n            String args = null;\n\n            int argStart = cl.indexOf(\"<\");\n            if (argStart >= 0) {\n              name = cl.substring(0, argStart);\n              args = cl.substring(argStart + 1, cl.length() - 1);\n            }\n\n            if (cl.length() < signature.length()) {\n              signature = signature.substring(cl.length() + 1); // skip '.'\n              GenericType type11 = new GenericType(CodeConstants.TYPE_OBJECT, 0, name);\n              parseArgumentsList(args, type11);\n              enclosingClasses.add(type11);\n            }\n            else {\n              value = name;\n              parseArgumentsList(args, this);\n              break;\n            }\n          }\n\n          break loop;\n\n        default:\n          value = signature.substring(index, index + 1);\n          type = VarType.getType(value.charAt(0));\n      }\n\n      index++;\n    }\n\n    this.type = type;\n    this.arrayDim = arrayDim;\n    this.value = value;\n  }\n\n  @Override\n  public int getType() {\n    return type;\n  }\n\n  @Override\n  public int getArrayDim() {\n    return arrayDim;\n  }\n\n  @Override\n  public String getValue() {\n    return value;\n  }\n\n  private static String getNextClassSignature(String value) {\n    int counter = 0;\n    int index = 0;\n\n    loop:\n    while (index < value.length()) {\n      switch (value.charAt(index)) {\n        case '<' -> counter++;\n        case '>' -> counter--;\n        case '.' -> {\n          if (counter == 0) {\n            break loop;\n          }\n        }\n      }\n\n      index++;\n    }\n\n    return value.substring(0, index);\n  }\n\n  private static void parseArgumentsList(String value, GenericType type) {\n    if (value == null) {\n      return;\n    }\n\n    while (value.length() > 0) {\n      String typeStr = getNextType(value);\n      int len = typeStr.length();\n      int wildcard = switch (typeStr.charAt(0)) {\n        case '*' -> WILDCARD_UNBOUND;\n        case '+' -> WILDCARD_EXTENDS;\n        case '-' -> WILDCARD_SUPER;\n        default -> WILDCARD_NO;\n      };\n\n      type.getWildcards().add(wildcard);\n\n      if (wildcard != WILDCARD_NO) {\n        typeStr = typeStr.substring(1);\n      }\n\n      type.getArguments().add(typeStr.length() == 0 ? null : new GenericType(typeStr));\n\n      value = value.substring(len);\n    }\n  }\n\n  public static String getNextType(String value) {\n    int counter = 0;\n    int index = 0;\n\n    boolean contMode = false;\n\n    loop:\n    while (index < value.length()) {\n      switch (value.charAt(index)) {\n        case '*':\n          if (!contMode) {\n            break loop;\n          }\n          break;\n        case 'L':\n        case 'T':\n          if (!contMode) {\n            contMode = true;\n          }\n        case '[':\n        case '+':\n        case '-':\n          break;\n        default:\n          if (!contMode) {\n            break loop;\n          }\n          break;\n        case '<':\n          counter++;\n          break;\n        case '>':\n          counter--;\n          break;\n        case ';':\n          if (counter == 0) {\n            break loop;\n          }\n      }\n\n      index++;\n    }\n\n    return value.substring(0, index + 1);\n  }\n\n  public GenericType decreaseArrayDim() {\n    assert getArrayDim() > 0 : this;\n    return new GenericType(this, getArrayDim() - 1);\n  }\n\n  public List<GenericType> getArguments() {\n    return arguments;\n  }\n\n  public List<GenericType> getEnclosingClasses() {\n    return enclosingClasses;\n  }\n\n  public List<Integer> getWildcards() {\n    return wildcards;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.lazy;\n\nimport org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;\nimport org.jetbrains.java.decompiler.struct.StructClass;\nimport org.jetbrains.java.decompiler.struct.StructMethod;\nimport org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;\nimport org.jetbrains.java.decompiler.struct.consts.ConstantPool;\nimport org.jetbrains.java.decompiler.util.DataInputFullStream;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class LazyLoader {\n  private final Map<String, Link> mapClassLinks = new HashMap<>();\n  private final IBytecodeProvider provider;\n\n  public LazyLoader(IBytecodeProvider provider) {\n    this.provider = provider;\n  }\n\n  public void addClassLink(String className, Link link) {\n    mapClassLinks.put(className, link);\n  }\n\n  public void removeClassLink(String className) {\n    mapClassLinks.remove(className);\n  }\n\n  public Link getClassLink(String className) {\n    return mapClassLinks.get(className);\n  }\n\n  public ConstantPool loadPool(String className) {\n    try (DataInputFullStream in = getClassStream(className)) {\n      if (in != null) {\n        in.discard(8);\n        return new ConstantPool(in);\n      }\n\n      return null;\n    }\n    catch (IOException ex) {\n      throw new RuntimeException(ex);\n    }\n  }\n\n  public byte[] loadBytecode(StructClass classStruct, StructMethod mt, int codeFullLength) {\n    String className = classStruct.qualifiedName;\n\n    try (DataInputFullStream in = getClassStream(className)) {\n      if (in != null) {\n        in.discard(8);\n\n        ConstantPool pool = classStruct.getPool();\n        if (pool == null) {\n          pool = new ConstantPool(in);\n        }\n        else {\n          ConstantPool.skipPool(in);\n        }\n\n        in.discard(6);\n\n        // interfaces\n        in.discard(in.readUnsignedShort() * 2);\n\n        // fields\n        int size = in.readUnsignedShort();\n        for (int i = 0; i < size; i++) {\n          in.discard(6);\n          skipAttributes(in);\n        }\n\n        // methods\n        size = in.readUnsignedShort();\n        for (int i = 0; i < size; i++) {\n          in.discard(2);\n\n          int nameIndex = in.readUnsignedShort();\n          int descriptorIndex = in.readUnsignedShort();\n\n          String[] values = pool.getClassElement(ConstantPool.METHOD, className, nameIndex, descriptorIndex);\n          if (!mt.getName().equals(values[0]) || !mt.getDescriptor().equals(values[1])) {\n            skipAttributes(in);\n            continue;\n          }\n\n          int attrSize = in.readUnsignedShort();\n          for (int j = 0; j < attrSize; j++) {\n            int attrNameIndex = in.readUnsignedShort();\n            String attrName = pool.getPrimitiveConstant(attrNameIndex).getString();\n            if (!StructGeneralAttribute.ATTRIBUTE_CODE.name.equals(attrName)) {\n              in.discard(in.readInt());\n              continue;\n            }\n\n            in.discard(12);\n\n            return in.read(codeFullLength);\n          }\n\n          break;\n        }\n      }\n\n      return null;\n    }\n    catch (IOException ex) {\n      throw new RuntimeException(ex);\n    }\n  }\n\n  public DataInputFullStream getClassStream(String externalPath, String internalPath) throws IOException {\n    byte[] bytes = provider.getBytecode(externalPath, internalPath);\n    return new DataInputFullStream(bytes);\n  }\n\n  public DataInputFullStream getClassStream(String qualifiedClassName) throws IOException {\n    Link link = mapClassLinks.get(qualifiedClassName);\n    return link == null ? null : getClassStream(link.externalPath, link.internalPath);\n  }\n\n  public static void skipAttributes(DataInputFullStream in) throws IOException {\n    int length = in.readUnsignedShort();\n    for (int i = 0; i < length; i++) {\n      in.discard(2);\n      in.discard(in.readInt());\n    }\n  }\n\n  public static class Link {\n    public final String externalPath;\n    public final String internalPath;\n\n    public Link(String externalPath, String internalPath) {\n      this.externalPath = externalPath;\n      this.internalPath = internalPath;\n    }\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/match/IMatchable.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.match;\n\npublic interface IMatchable {\n  enum MatchProperties {\n    STATEMENT_TYPE,\n    STATEMENT_RET,\n    STATEMENT_STATSIZE,\n    STATEMENT_EXPRSIZE,\n    STATEMENT_POSITION,\n    STATEMENT_IFTYPE,\n\n    EXPRENT_TYPE,\n    EXPRENT_RET,\n    EXPRENT_POSITION,\n    EXPRENT_FUNCTYPE,\n    EXPRENT_EXITTYPE,\n    EXPRENT_CONSTTYPE,\n    EXPRENT_CONSTVALUE,\n    EXPRENT_INVOCATION_CLASS,\n    EXPRENT_INVOCATION_SIGNATURE,\n    EXPRENT_INVOCATION_PARAMETER,\n    EXPRENT_VAR_INDEX,\n    EXPRENT_FIELD_NAME,\n  }\n\n  IMatchable findObject(MatchNode matchNode, int index);\n\n  boolean match(MatchNode matchNode, MatchEngine engine);\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.match;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;\nimport org.jetbrains.java.decompiler.modules.decompiler.stats.Statement.StatementType;\nimport org.jetbrains.java.decompiler.struct.gen.VarType;\nimport org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;\nimport org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;\n\nimport java.util.*;\n\nimport static java.util.Map.entry;\n\npublic class MatchEngine {\n  @SuppressWarnings(\"SpellCheckingInspection\")\n  private static final Map<String, MatchProperties> stat_properties = Map.of(\n    \"type\", MatchProperties.STATEMENT_TYPE,\n    \"ret\", MatchProperties.STATEMENT_RET,\n    \"position\", MatchProperties.STATEMENT_POSITION,\n    \"statsize\", MatchProperties.STATEMENT_STATSIZE,\n    \"exprsize\", MatchProperties.STATEMENT_EXPRSIZE,\n    \"iftype\", MatchProperties.STATEMENT_IFTYPE);\n\n  @SuppressWarnings(\"SpellCheckingInspection\")\n  private static final Map<String, MatchProperties> expr_properties = Map.ofEntries(\n    entry(\"type\", MatchProperties.EXPRENT_TYPE),\n    entry(\"ret\", MatchProperties.EXPRENT_RET),\n    entry(\"position\", MatchProperties.EXPRENT_POSITION),\n    entry(\"functype\", MatchProperties.EXPRENT_FUNCTYPE),\n    entry(\"exittype\", MatchProperties.EXPRENT_EXITTYPE),\n    entry(\"consttype\", MatchProperties.EXPRENT_CONSTTYPE),\n    entry(\"constvalue\", MatchProperties.EXPRENT_CONSTVALUE),\n    entry(\"invclass\", MatchProperties.EXPRENT_INVOCATION_CLASS),\n    entry(\"signature\", MatchProperties.EXPRENT_INVOCATION_SIGNATURE),\n    entry(\"parameter\", MatchProperties.EXPRENT_INVOCATION_PARAMETER),\n    entry(\"index\", MatchProperties.EXPRENT_VAR_INDEX),\n    entry(\"name\", MatchProperties.EXPRENT_FIELD_NAME));\n\n  @SuppressWarnings(\"SpellCheckingInspection\")\n  private static final Map<String, StatementType> stat_type = Map.of(\n    \"if\", StatementType.IF,\n    \"do\", StatementType.DO,\n    \"switch\", StatementType.SWITCH,\n    \"trycatch\", StatementType.TRY_CATCH,\n    \"basicblock\", StatementType.BASIC_BLOCK,\n    \"sequence\", StatementType.SEQUENCE);\n\n  private static final Map<String, Integer> expr_type = Map.ofEntries(\n    entry(\"array\", Exprent.EXPRENT_ARRAY),\n    entry(\"assignment\", Exprent.EXPRENT_ASSIGNMENT),\n    entry(\"constant\", Exprent.EXPRENT_CONST),\n    entry(\"exit\", Exprent.EXPRENT_EXIT),\n    entry(\"field\", Exprent.EXPRENT_FIELD),\n    entry(\"function\", Exprent.EXPRENT_FUNCTION),\n    entry(\"if\", Exprent.EXPRENT_IF),\n    entry(\"invocation\", Exprent.EXPRENT_INVOCATION),\n    entry(\"monitor\", Exprent.EXPRENT_MONITOR),\n    entry(\"new\", Exprent.EXPRENT_NEW),\n    entry(\"switch\", Exprent.EXPRENT_SWITCH),\n    entry(\"var\", Exprent.EXPRENT_VAR),\n    entry(\"annotation\", Exprent.EXPRENT_ANNOTATION),\n    entry(\"assert\", Exprent.EXPRENT_ASSERT));\n\n  private static final Map<String, Integer> expr_func_type = Map.of(\"eq\", FunctionExprent.FUNCTION_EQ);\n\n  private static final Map<String, Integer> expr_exit_type = Map.of(\n    \"return\", ExitExprent.EXIT_RETURN,\n    \"throw\", ExitExprent.EXIT_THROW);\n\n  @SuppressWarnings(\"SpellCheckingInspection\")\n  private static final Map<String, Integer> stat_if_type = Map.of(\n    \"if\", IfStatement.IFTYPE_IF,\n    \"ifelse\", IfStatement.IFTYPE_IFELSE);\n\n  private static final Map<String, VarType> expr_const_type = Map.of(\n    \"null\", VarType.VARTYPE_NULL,\n    \"string\", VarType.VARTYPE_STRING);\n\n  private final MatchNode rootNode;\n  private final Map<String, Object> variables = new HashMap<>();\n\n  public MatchEngine(String description) {\n    // each line is a separate statement/expression\n    String[] lines = description.split(\"\\n\");\n\n    int depth = 0;\n    LinkedList<MatchNode> stack = new LinkedList<>();\n\n    for (String line : lines) {\n      List<String> properties = new ArrayList<>(Arrays.asList(line.split(\"\\\\s+\"))); // split on any number of whitespaces\n      if (properties.get(0).isEmpty()) {\n        properties.remove(0);\n      }\n\n      int node_type = \"statement\".equals(properties.get(0)) ? MatchNode.MATCHNODE_STATEMENT : MatchNode.MATCHNODE_EXPRENT;\n\n      // create new node\n      MatchNode matchNode = new MatchNode(node_type);\n      for (int i = 1; i < properties.size(); ++i) {\n        String[] values = properties.get(i).split(\":\");\n\n        MatchProperties property = (node_type == MatchNode.MATCHNODE_STATEMENT ? stat_properties : expr_properties).get(values[0]);\n        if (property == null) { // unknown property defined\n          throw new RuntimeException(\"Unknown matching property\");\n        }\n        else {\n          Object value;\n          int parameter = 0;\n\n          String strValue = values[1];\n          if (values.length == 3) {\n            parameter = Integer.parseInt(values[1]);\n            strValue = values[2];\n          }\n\n          value = switch (property) {\n            case STATEMENT_TYPE -> stat_type.get(strValue);\n            case STATEMENT_STATSIZE, STATEMENT_EXPRSIZE -> Integer.valueOf(strValue);\n            case STATEMENT_POSITION, EXPRENT_POSITION, EXPRENT_INVOCATION_CLASS, EXPRENT_INVOCATION_SIGNATURE, EXPRENT_INVOCATION_PARAMETER, EXPRENT_VAR_INDEX, EXPRENT_FIELD_NAME, EXPRENT_CONSTVALUE, STATEMENT_RET, EXPRENT_RET ->\n              strValue;\n            case STATEMENT_IFTYPE -> stat_if_type.get(strValue);\n            case EXPRENT_FUNCTYPE -> expr_func_type.get(strValue);\n            case EXPRENT_EXITTYPE -> expr_exit_type.get(strValue);\n            case EXPRENT_CONSTTYPE -> expr_const_type.get(strValue);\n            case EXPRENT_TYPE -> expr_type.get(strValue);\n          };\n\n          matchNode.addRule(property, new RuleValue(parameter, value));\n        }\n      }\n\n      if (stack.isEmpty()) { // first line, root node\n        stack.push(matchNode);\n      }\n      else {\n        // return to the correct parent on the stack\n        int new_depth = line.lastIndexOf(' ', depth) + 1;\n        for (int i = new_depth; i <= depth; ++i) {\n          stack.pop();\n        }\n\n        // insert new node\n        stack.getFirst().addChild(matchNode);\n        stack.push(matchNode);\n\n        depth = new_depth;\n      }\n    }\n\n    this.rootNode = stack.getLast();\n  }\n\n  public boolean match(IMatchable object) {\n    variables.clear();\n    return match(this.rootNode, object);\n  }\n\n  private boolean match(MatchNode matchNode, IMatchable object) {\n    if (!object.match(matchNode, this)) {\n      return false;\n    }\n\n    int expr_index = 0;\n    int stat_index = 0;\n    for (MatchNode childNode : matchNode.getChildren()) {\n      boolean isStatement = childNode.getType() == MatchNode.MATCHNODE_STATEMENT;\n\n      IMatchable childObject = object.findObject(childNode, isStatement ? stat_index : expr_index);\n      if (childObject == null || !match(childNode, childObject)) {\n        return false;\n      }\n\n      if (isStatement) {\n        stat_index++;\n      }\n      else {\n        expr_index++;\n      }\n    }\n\n    return true;\n  }\n\n  public boolean checkAndSetVariableValue(String name, Object value) {\n    Object old_value = variables.get(name);\n    if (old_value != null) {\n      return old_value.equals(value);\n    }\n    else {\n      variables.put(name, value);\n      return true;\n    }\n  }\n\n  public Object getVariableValue(String name) {\n    return variables.get(name);\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/struct/match/MatchNode.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.struct.match;\n\nimport org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class MatchNode {\n  public static class RuleValue {\n    public final int parameter;\n    public final Object value;\n\n    public RuleValue(int parameter, Object value) {\n      this.parameter = parameter;\n      this.value = value;\n    }\n\n    public boolean isVariable() {\n      String strValue = value.toString();\n      return (strValue.charAt(0) == '$' && strValue.charAt(strValue.length() - 1) == '$');\n    }\n\n    public String toString() {\n      return value.toString();\n    }\n  }\n\n  public static final int MATCHNODE_STATEMENT = 0;\n  public static final int MATCHNODE_EXPRENT = 1;\n\n  private final int type;\n  private final Map<MatchProperties, RuleValue> rules = new HashMap<>();\n  private final List<MatchNode> children = new ArrayList<>();\n\n  public MatchNode(int type) {\n    this.type = type;\n  }\n\n  public void addChild(MatchNode child) {\n    children.add(child);\n  }\n\n  public void addRule(MatchProperties property, RuleValue value) {\n    rules.put(property, value);\n  }\n\n  public int getType() {\n    return type;\n  }\n\n  public List<MatchNode> getChildren() {\n    return children;\n  }\n\n  public Map<MatchProperties, RuleValue> getRules() {\n    return rules;\n  }\n\n  public Object getRuleValue(MatchProperties property) {\n    RuleValue rule = rules.get(property);\n    return rule == null ? null : rule.value;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/DataInputFullStream.java",
    "content": "// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.util;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.DataInputStream;\nimport java.io.IOException;\n\npublic class DataInputFullStream extends DataInputStream {\n  public DataInputFullStream(byte[] bytes) {\n    super(new ByteArrayInputStream(bytes));\n  }\n\n  public byte[] read(int n) throws IOException {\n    return InterpreterUtil.readBytes(this, n);\n  }\n\n  public void discard(int n) throws IOException {\n    InterpreterUtil.discardBytes(this, n);\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.util;\n\nimport java.util.*;\n\npublic class FastFixedSetFactory<E> {\n\n  private final VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<>();\n\n  private final int dataLength;\n\n  public FastFixedSetFactory(Collection<E> set) {\n\n    dataLength = set.size() / 32 + 1;\n\n    int index = 0;\n    int mask = 1;\n\n    for (E element : set) {\n\n      int block = index / 32;\n\n      if (index % 32 == 0) {\n        mask = 1;\n      }\n\n      colValuesInternal.putWithKey(new int[]{block, mask}, element);\n\n      index++;\n      mask <<= 1;\n    }\n  }\n\n  public FastFixedSet<E> spawnEmptySet() {\n    return new FastFixedSet<>(this);\n  }\n\n  private int getDataLength() {\n    return dataLength;\n  }\n\n  private VBStyleCollection<int[], E> getInternalValuesCollection() {\n    return colValuesInternal;\n  }\n\n  public static final class FastFixedSet<E> implements Iterable<E> {\n\n    private final FastFixedSetFactory<E> factory;\n\n    private final VBStyleCollection<int[], E> colValuesInternal;\n\n    private int[] data;\n\n\n    private FastFixedSet(FastFixedSetFactory<E> factory) {\n      this.factory = factory;\n      this.colValuesInternal = factory.getInternalValuesCollection();\n      this.data = new int[factory.getDataLength()];\n    }\n\n    public FastFixedSet<E> getCopy() {\n\n      FastFixedSet<E> copy = new FastFixedSet<>(factory);\n\n      int arrlength = data.length;\n      int[] cpdata = Arrays.copyOf(data, arrlength);\n      copy.setData(cpdata);\n\n      return copy;\n    }\n\n    public void setAllElements() {\n\n      int[] lastindex = colValuesInternal.get(colValuesInternal.size() - 1);\n\n      for (int i = lastindex[0] - 1; i >= 0; i--) {\n        data[i] = 0xFFFFFFFF;\n      }\n\n      data[lastindex[0]] = lastindex[1] | (lastindex[1] - 1);\n    }\n\n    public void add(E element) {\n      int[] index = colValuesInternal.getWithKey(element);\n      data[index[0]] |= index[1];\n    }\n\n    public void addAll(Collection<E> set) {\n      for (E element : set) {\n        add(element);\n      }\n    }\n\n    public void remove(E element) {\n      int[] index = colValuesInternal.getWithKey(element);\n      data[index[0]] &= ~index[1];\n    }\n\n    public boolean contains(E element) {\n      int[] index = colValuesInternal.getWithKey(element);\n      return (data[index[0]] & index[1]) != 0;\n    }\n\n    public boolean contains(FastFixedSet<E> set) {\n      int[] extdata = set.getData();\n      int[] intdata = data;\n\n      for (int i = intdata.length - 1; i >= 0; i--) {\n        if ((extdata[i] & ~intdata[i]) != 0) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    public void union(FastFixedSet<E> set) {\n      int[] extdata = set.getData();\n      int[] intdata = data;\n\n      for (int i = intdata.length - 1; i >= 0; i--) {\n        intdata[i] |= extdata[i];\n      }\n    }\n\n    public void intersection(FastFixedSet<E> set) {\n      int[] extdata = set.getData();\n      int[] intdata = data;\n\n      for (int i = intdata.length - 1; i >= 0; i--) {\n        intdata[i] &= extdata[i];\n      }\n    }\n\n    public void complement(FastFixedSet<E> set) {\n      int[] extdata = set.getData();\n      int[] intdata = data;\n\n      for (int i = intdata.length - 1; i >= 0; i--) {\n        intdata[i] &= ~extdata[i];\n      }\n    }\n\n\n    public boolean equals(Object o) {\n      if (o == this) return true;\n      if (!(o instanceof FastFixedSet)) return false;\n\n      int[] extdata = ((FastFixedSet<?>)o).getData();\n      int[] intdata = data;\n\n      for (int i = intdata.length - 1; i >= 0; i--) {\n        if (intdata[i] != extdata[i]) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    public boolean isEmpty() {\n      int[] intdata = data;\n\n      for (int i = intdata.length - 1; i >= 0; i--) {\n        if (intdata[i] != 0) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    @Override\n    public Iterator<E> iterator() {\n      return new FastFixedSetIterator<>(this);\n    }\n\n    public Set<E> toPlainSet() {\n      return toPlainCollection(new HashSet<>());\n    }\n\n    private <T extends Collection<E>> T toPlainCollection(T cl) {\n\n      int[] intdata = data;\n      for (int bindex = 0; bindex < intdata.length; bindex++) {\n        int block = intdata[bindex];\n        if (block != 0) {\n          int index = bindex << 5; // * 32\n          for (int i = 31; i >= 0; i--) {\n            if ((block & 1) != 0) {\n              cl.add(colValuesInternal.getKey(index));\n            }\n            index++;\n            block >>>= 1;\n          }\n        }\n      }\n\n      return cl;\n    }\n\n    public String toString() {\n\n      StringBuilder buffer = new StringBuilder(\"{\");\n\n      int[] intdata = data;\n      boolean first = true;\n\n      for (int i = colValuesInternal.size() - 1; i >= 0; i--) {\n        int[] index = colValuesInternal.get(i);\n\n        if ((intdata[index[0]] & index[1]) != 0) {\n          if (first) {\n            first = false;\n          }\n          else {\n            buffer.append(\",\");\n          }\n          buffer.append(colValuesInternal.getKey(i));\n        }\n      }\n\n      buffer.append(\"}\");\n\n      return buffer.toString();\n    }\n\n    private int[] getData() {\n      return data;\n    }\n\n    private void setData(int[] data) {\n      this.data = data;\n    }\n\n    public FastFixedSetFactory<E> getFactory() {\n      return factory;\n    }\n  }\n\n  public static final class FastFixedSetIterator<E> implements Iterator<E> {\n\n    private final VBStyleCollection<int[], E> colValuesInternal;\n    private final int[] data;\n    private final int size;\n\n    private int pointer = -1;\n    private int next_pointer = -1;\n\n    private FastFixedSetIterator(FastFixedSet<E> set) {\n      colValuesInternal = set.getFactory().getInternalValuesCollection();\n      data = set.getData();\n      size = colValuesInternal.size();\n    }\n\n    private int getNextIndex(int index) {\n\n      index++;\n      int ret = index;\n      int bindex = index / 32;\n      int dindex = index % 32;\n\n      while (bindex < data.length) {\n        int block = data[bindex];\n\n        if (block != 0) {\n          block >>>= dindex;\n          while (dindex < 32) {\n            if ((block & 1) != 0) {\n              return ret;\n            }\n            block >>>= 1;\n            dindex++;\n            ret++;\n          }\n        }\n        else {\n          ret += (32 - dindex);\n        }\n\n        dindex = 0;\n        bindex++;\n      }\n\n      return -1;\n    }\n\n    @Override\n    public boolean hasNext() {\n      next_pointer = getNextIndex(pointer);\n      return (next_pointer >= 0);\n    }\n\n    @Override\n    public E next() {\n      if (next_pointer >= 0) {\n        pointer = next_pointer;\n      }\n      else {\n        pointer = getNextIndex(pointer);\n        if (pointer == -1) {\n          pointer = size;\n        }\n      }\n\n      next_pointer = -1;\n      return pointer < size ? colValuesInternal.getKey(pointer) : null;\n    }\n\n    @Override\n    public void remove() {\n      int[] index = colValuesInternal.get(pointer);\n      data[index[0]] &= ~index[1];\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java",
    "content": "// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.util;\n\nimport java.util.*;\n\npublic class FastSparseSetFactory<E> {\n\n  private final VBStyleCollection<int[], E> colValuesInternal = new VBStyleCollection<>();\n\n  private int lastBlock;\n\n  private int lastMask;\n\n  public FastSparseSetFactory(Collection<? extends E> set) {\n\n    int block = -1;\n    int mask = -1;\n    int index = 0;\n\n    for (E element : set) {\n\n      block = index / 32;\n\n      if (index % 32 == 0) {\n        mask = 1;\n      }\n      else {\n        mask <<= 1;\n      }\n\n      colValuesInternal.putWithKey(new int[]{block, mask}, element);\n\n      index++;\n    }\n\n    lastBlock = block;\n    lastMask = mask;\n  }\n\n  private int[] addElement(E element) {\n\n    if (lastMask == -1 || lastMask == 0x80000000) {\n      lastMask = 1;\n      lastBlock++;\n    }\n    else {\n      lastMask <<= 1;\n    }\n\n    int[] pointer = new int[]{lastBlock, lastMask};\n    colValuesInternal.putWithKey(pointer, element);\n\n    return pointer;\n  }\n\n  public FastSparseSet<E> spawnEmptySet() {\n    return new FastSparseSet<>(this);\n  }\n\n  public int getLastBlock() {\n    return lastBlock;\n  }\n\n  private VBStyleCollection<int[], E> getInternalValuesCollection() {\n    return colValuesInternal;\n  }\n\n\n  public static final class FastSparseSet<E> implements Iterable<E> {\n    public static final FastSparseSet[] EMPTY_ARRAY = new FastSparseSet[0];\n\n    private final FastSparseSetFactory<E> factory;\n\n    private final VBStyleCollection<int[], E> colValuesInternal;\n\n    private int[] data;\n    private int[] next;\n\n    private FastSparseSet(FastSparseSetFactory<E> factory) {\n      this.factory = factory;\n      this.colValuesInternal = factory.getInternalValuesCollection();\n\n      int length = factory.getLastBlock() + 1;\n      this.data = new int[length];\n      this.next = new int[length];\n    }\n\n    private FastSparseSet(FastSparseSetFactory<E> factory, int[] data, int[] next) {\n      this.factory = factory;\n      this.colValuesInternal = factory.getInternalValuesCollection();\n\n      this.data = data;\n      this.next = next;\n    }\n\n    public FastSparseSet<E> getCopy() {\n      return new FastSparseSet<>(factory, data.clone(), next.clone());\n    }\n\n    private int[] ensureCapacity(int index) {\n\n      int newlength = data.length;\n      if (newlength == 0) {\n        newlength = 1;\n      }\n\n      while (newlength <= index) {\n        newlength *= 2;\n      }\n\n      data = Arrays.copyOf(data, newlength);\n      next = Arrays.copyOf(next, newlength);\n\n      return data;\n    }\n\n    public void add(E element) {\n      int[] index = colValuesInternal.getWithKey(element);\n\n      if (index == null) {\n        index = factory.addElement(element);\n      }\n\n      int block = index[0];\n      if (block >= data.length) {\n        ensureCapacity(block);\n      }\n\n      data[block] |= index[1];\n\n      changeNext(next, block, next[block], block);\n    }\n\n    public void remove(E element) {\n      int[] index = colValuesInternal.getWithKey(element);\n\n      if (index == null) {\n        index = factory.addElement(element);\n      }\n\n      int block = index[0];\n      if (block < data.length) {\n        data[block] &= ~index[1];\n\n        if (data[block] == 0) {\n          changeNext(next, block, block, next[block]);\n        }\n      }\n    }\n\n    public boolean contains(E element) {\n      int[] index = colValuesInternal.getWithKey(element);\n\n      if (index == null) {\n        index = factory.addElement(element);\n      }\n\n      return index[0] < data.length && ((data[index[0]] & index[1]) != 0);\n    }\n\n    private void setNext() {\n\n      int link = 0;\n      for (int i = data.length - 1; i >= 0; i--) {\n        next[i] = link;\n        if (data[i] != 0) {\n          link = i;\n        }\n      }\n    }\n\n    private static void changeNext(int[] arrnext, int key, int oldnext, int newnext) {\n      for (int i = key - 1; i >= 0; i--) {\n        if (arrnext[i] == oldnext) {\n          arrnext[i] = newnext;\n        }\n        else {\n          break;\n        }\n      }\n    }\n\n    public void union(FastSparseSet<E> set) {\n\n      int[] extdata = set.getData();\n      int[] extnext = set.getNext();\n      int[] intdata = data;\n      int intlength = intdata.length;\n\n      int pointer = 0;\n      do {\n        if (pointer >= intlength) {\n          intdata = ensureCapacity(extdata.length - 1);\n        }\n\n        boolean nextrec = (intdata[pointer] == 0);\n        intdata[pointer] |= extdata[pointer];\n\n        if (nextrec) {\n          changeNext(next, pointer, next[pointer], pointer);\n        }\n\n        pointer = extnext[pointer];\n      }\n      while (pointer != 0);\n    }\n\n    public void intersection(FastSparseSet<E> set) {\n      int[] extdata = set.getData();\n      int[] intdata = data;\n\n      int minlength = Math.min(extdata.length, intdata.length);\n\n      for (int i = minlength - 1; i >= 0; i--) {\n        intdata[i] &= extdata[i];\n      }\n\n      for (int i = intdata.length - 1; i >= minlength; i--) {\n        intdata[i] = 0;\n      }\n\n      setNext();\n    }\n\n    public void complement(FastSparseSet<E> set) {\n\n      int[] extdata = set.getData();\n      int[] intdata = data;\n      int extlength = extdata.length;\n\n      int pointer = 0;\n      do {\n        if (pointer >= extlength) {\n          break;\n        }\n\n        intdata[pointer] &= ~extdata[pointer];\n        if (intdata[pointer] == 0) {\n          changeNext(next, pointer, pointer, next[pointer]);\n        }\n\n        pointer = next[pointer];\n      }\n      while (pointer != 0);\n    }\n\n\n    public boolean equals(Object o) {\n      if (o == this) return true;\n      if (!(o instanceof FastSparseSet)) return false;\n\n      int[] longdata = ((FastSparseSet<?>)o).getData();\n      int[] shortdata = data;\n\n      if (data.length > longdata.length) {\n        shortdata = longdata;\n        longdata = data;\n      }\n\n      for (int i = shortdata.length - 1; i >= 0; i--) {\n        if (shortdata[i] != longdata[i]) {\n          return false;\n        }\n      }\n\n      for (int i = longdata.length - 1; i >= shortdata.length; i--) {\n        if (longdata[i] != 0) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    public int getCardinality() {\n\n      boolean found = false;\n      int[] intdata = data;\n\n      for (int i = intdata.length - 1; i >= 0; i--) {\n        int block = intdata[i];\n        if (block != 0) {\n          if (found) {\n            return 2;\n          }\n          else {\n            if ((block & (block - 1)) == 0) {\n              found = true;\n            }\n            else {\n              return 2;\n            }\n          }\n        }\n      }\n\n      return found ? 1 : 0;\n    }\n\n    public boolean isEmpty() {\n      return data.length == 0 || (next[0] == 0 && data[0] == 0);\n    }\n\n    @Override\n    public Iterator<E> iterator() {\n      return new FastSparseSetIterator<>(this);\n    }\n\n    public Set<E> toPlainSet() {\n      HashSet<E> set = new HashSet<>();\n\n      int[] intdata = data;\n\n      int size = data.length * 32;\n      if (size > colValuesInternal.size()) {\n        size = colValuesInternal.size();\n      }\n\n      for (int i = size - 1; i >= 0; i--) {\n        int[] index = colValuesInternal.get(i);\n\n        if ((intdata[index[0]] & index[1]) != 0) {\n          set.add(colValuesInternal.getKey(i));\n        }\n      }\n\n      return set;\n    }\n\n    public String toString() {\n      return toPlainSet().toString();\n    }\n\n    private int[] getData() {\n      return data;\n    }\n\n    private int[] getNext() {\n      return next;\n    }\n\n    public FastSparseSetFactory<E> getFactory() {\n      return factory;\n    }\n  }\n\n  public static final class FastSparseSetIterator<E> implements Iterator<E> {\n\n    private final VBStyleCollection<int[], E> colValuesInternal;\n    private final int[] data;\n    private final int[] next;\n    private final int size;\n\n    private int pointer = -1;\n    private int next_pointer = -1;\n\n    private FastSparseSetIterator(FastSparseSet<E> set) {\n      colValuesInternal = set.getFactory().getInternalValuesCollection();\n      data = set.getData();\n      next = set.getNext();\n      size = colValuesInternal.size();\n    }\n\n    private int getNextIndex(int index) {\n\n      index++;\n      int bindex = index >>> 5;\n      int dindex = index & 0x1F;\n\n      while (bindex < data.length) {\n        int block = data[bindex];\n\n        if (block != 0) {\n          block >>>= dindex;\n          while (dindex < 32) {\n            if ((block & 1) != 0) {\n              return (bindex << 5) + dindex;\n            }\n            block >>>= 1;\n            dindex++;\n          }\n        }\n\n        dindex = 0;\n        bindex = next[bindex];\n\n        if (bindex == 0) {\n          break;\n        }\n      }\n\n      return -1;\n    }\n\n    @Override\n    public boolean hasNext() {\n      next_pointer = getNextIndex(pointer);\n      return (next_pointer >= 0);\n    }\n\n    @Override\n    public E next() {\n      if (next_pointer >= 0) {\n        pointer = next_pointer;\n      }\n      else {\n        pointer = getNextIndex(pointer);\n        if (pointer == -1) {\n          pointer = size;\n        }\n      }\n\n      next_pointer = -1;\n      return pointer < size ? colValuesInternal.getKey(pointer) : null;\n    }\n\n    @Override\n    public void remove() {\n      int[] index = colValuesInternal.get(pointer);\n      data[index[0]] &= ~index[1];\n    }\n  }\n}\n\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/InterpreterUtil.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.util;\n\nimport java.io.*;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\npublic final class InterpreterUtil {\n  public static final boolean IS_WINDOWS = System.getProperty(\"os.name\", \"\").startsWith(\"Windows\");\n\n  public static final int[] EMPTY_INT_ARRAY = new int[0];\n\n  private static final int BUFFER_SIZE = 16 * 1024;\n\n  public static void copyFile(File source, File target) throws IOException {\n    try (FileInputStream in = new FileInputStream(source); FileOutputStream out = new FileOutputStream(target)) {\n      copyStream(in, out);\n    }\n  }\n\n  public static void copyStream(InputStream in, OutputStream out) throws IOException {\n    byte[] buffer = new byte[BUFFER_SIZE];\n    int len;\n    while ((len = in.read(buffer)) >= 0) {\n      out.write(buffer, 0, len);\n    }\n  }\n\n  public static byte[] getBytes(ZipFile archive, ZipEntry entry) throws IOException {\n    try (InputStream stream = archive.getInputStream(entry)) {\n      return readBytes(stream, (int)entry.getSize());\n    }\n  }\n\n  public static byte[] getBytes(File file) throws IOException {\n    try (FileInputStream stream = new FileInputStream(file)) {\n      return readBytes(stream, (int)file.length());\n    }\n  }\n\n  public static byte[] readBytes(InputStream stream, int length) throws IOException {\n    byte[] bytes = new byte[length];\n\n    int n = 0, off = 0;\n    while (n < length) {\n      int count = stream.read(bytes, off + n, length - n);\n      if (count < 0) {\n        throw new IOException(\"premature end of stream\");\n      }\n      n += count;\n    }\n\n    return bytes;\n  }\n\n  public static void discardBytes(InputStream stream, int length) throws IOException {\n    if (stream.skip(length) != length) {\n      throw new IOException(\"premature end of stream\");\n    }\n  }\n\n  public static boolean equalSets(Collection<?> c1, Collection<?> c2) {\n    if (c1 == null) {\n      return c2 == null;\n    }\n    else if (c2 == null) {\n      return false;\n    }\n\n    if (c1.size() != c2.size()) {\n      return false;\n    }\n\n    HashSet<Object> set = new HashSet<>(c1);\n    set.removeAll(c2);\n    return (set.size() == 0);\n  }\n\n  public static String makeUniqueKey(String name, String descriptor) {\n    return name + ' ' + descriptor;\n  }\n\n  public static String makeUniqueKey(String name, String descriptor1, String descriptor2) {\n    return name + ' ' + descriptor1 + ' ' + descriptor2;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/ListStack.java",
    "content": "// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.util;\n\nimport java.util.ArrayList;\n\npublic class ListStack<T> extends ArrayList<T> {\n  private int pointer = 0;\n\n  public ListStack() {\n    super();\n  }\n\n  public ListStack(int initialCapacity) {\n    super(initialCapacity);\n  }\n\n  private ListStack(ArrayList<? extends T> list) {\n    super(list);\n  }\n\n  public ListStack<T> copy() {\n    ListStack<T> copy = new ListStack<>(this);\n    copy.pointer = this.pointer;\n    return copy;\n  }\n\n  public void push(T item) {\n    this.add(item);\n    pointer++;\n  }\n\n  public T pop() {\n    pointer--;\n    T o = this.get(pointer);\n    this.remove(pointer);\n    return o;\n  }\n\n  public T pop(int count) {\n    T o = null;\n    for (int i = count; i > 0; i--) {\n      o = this.pop();\n    }\n    return o;\n  }\n\n  public void removeMultiple(int count) {\n    while (count > 0) {\n      pointer--;\n      this.remove(pointer);\n      count--;\n    }\n  }\n\n  public T getByOffset(int offset) {\n    return this.get(pointer + offset);\n  }\n\n  public void insertByOffset(int offset, T item) {\n    this.add(pointer + offset, item);\n    pointer++;\n  }\n\n  @Override\n  public void clear() {\n    super.clear();\n    pointer = 0;\n  }\n}\n"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.util;\n\nimport org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;\nimport org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\npublic class SFormsFastMapDirect {\n\n  private int size;\n\n  @SuppressWarnings(\"unchecked\") private final FastSparseSet<Integer>[][] elements = new FastSparseSet[3][];\n\n  private final int[][] next = new int[3][];\n\n  public SFormsFastMapDirect() {\n    this(true);\n  }\n\n  private SFormsFastMapDirect(boolean initialize) {\n    if (initialize) {\n      for (int i = 2; i >= 0; i--) {\n        @SuppressWarnings(\"unchecked\") FastSparseSet<Integer>[] empty = FastSparseSet.EMPTY_ARRAY;\n        elements[i] = empty;\n        next[i] = InterpreterUtil.EMPTY_INT_ARRAY;\n      }\n    }\n  }\n\n  public SFormsFastMapDirect(SFormsFastMapDirect map) {\n    for (int i = 2; i >= 0; i--) {\n      FastSparseSet<Integer>[] arr = map.elements[i];\n      int[] arrnext = map.next[i];\n\n      int length = arr.length;\n      @SuppressWarnings(\"unchecked\") FastSparseSet<Integer>[] arrnew = new FastSparseSet[length];\n      int[] arrnextnew = new int[length];\n\n      System.arraycopy(arr, 0, arrnew, 0, length);\n      System.arraycopy(arrnext, 0, arrnextnew, 0, length);\n\n      elements[i] = arrnew;\n      next[i] = arrnextnew;\n\n      size = map.size;\n    }\n  }\n\n  public SFormsFastMapDirect getCopy() {\n\n    SFormsFastMapDirect map = new SFormsFastMapDirect(false);\n    map.size = size;\n\n    FastSparseSet[][] mapelements = map.elements;\n    int[][] mapnext = map.next;\n\n    for (int i = 2; i >= 0; i--) {\n      FastSparseSet<Integer>[] arr = elements[i];\n      int length = arr.length;\n\n      if (length > 0) {\n        int[] arrnext = next[i];\n\n        @SuppressWarnings(\"unchecked\") FastSparseSet<Integer>[] arrnew = new FastSparseSet[length];\n        int[] arrnextnew = Arrays.copyOf(arrnext, length);\n\n        mapelements[i] = arrnew;\n        mapnext[i] = arrnextnew;\n\n        int pointer = 0;\n        do {\n          FastSparseSet<Integer> set = arr[pointer];\n          if (set != null) {\n            arrnew[pointer] = set.getCopy();\n          }\n\n          pointer = arrnext[pointer];\n        }\n        while (pointer != 0);\n      }\n      else {\n        mapelements[i] = FastSparseSet.EMPTY_ARRAY;\n        mapnext[i] = InterpreterUtil.EMPTY_INT_ARRAY;\n      }\n    }\n\n    return map;\n  }\n\n  public int size() {\n    return size;\n  }\n\n  public boolean isEmpty() {\n    return size == 0;\n  }\n\n  public void put(int key, FastSparseSet<Integer> value) {\n    putInternal(key, value, false);\n  }\n\n  public void removeAllFields() {\n    FastSparseSet<Integer>[] arr = elements[2];\n    int[] arrnext = next[2];\n\n    for (int i = arr.length - 1; i >= 0; i--) {\n      FastSparseSet<Integer> val = arr[i];\n      if (val != null) {\n        arr[i] = null;\n        size--;\n      }\n      arrnext[i] = 0;\n    }\n  }\n\n  public void putInternal(final int key, final FastSparseSet<Integer> value, boolean remove) {\n\n    int index = 0;\n    int ikey = key;\n    if (ikey < 0) {\n      index = 2;\n      ikey = -ikey;\n    }\n    else if (ikey >= VarExprent.STACK_BASE) {\n      index = 1;\n      ikey -= VarExprent.STACK_BASE;\n    }\n\n    FastSparseSet<Integer>[] arr = elements[index];\n    if (ikey >= arr.length) {\n      if (remove) {\n        return;\n      }\n      else {\n        arr = ensureCapacity(index, ikey + 1, false);\n      }\n    }\n\n    FastSparseSet<Integer> oldval = arr[ikey];\n    arr[ikey] = value;\n\n    int[] arrnext = next[index];\n\n    if (oldval == null && value != null) {\n      size++;\n      changeNext(arrnext, ikey, arrnext[ikey], ikey);\n    }\n    else if (oldval != null && value == null) {\n      size--;\n      changeNext(arrnext, ikey, ikey, arrnext[ikey]);\n    }\n  }\n\n  private static void changeNext(int[] arrnext, int key, int oldnext, int newnext) {\n    for (int i = key - 1; i >= 0; i--) {\n      if (arrnext[i] == oldnext) {\n        arrnext[i] = newnext;\n      }\n      else {\n        break;\n      }\n    }\n  }\n\n  public boolean containsKey(int key) {\n    return get(key) != null;\n  }\n\n  public FastSparseSet<Integer> get(int key) {\n\n    int index = 0;\n    if (key < 0) {\n      index = 2;\n      key = -key;\n    }\n    else if (key >= VarExprent.STACK_BASE) {\n      index = 1;\n      key -= VarExprent.STACK_BASE;\n    }\n\n    FastSparseSet<Integer>[] arr = elements[index];\n\n    if (key < arr.length) {\n      return arr[key];\n    }\n    return null;\n  }\n\n  public void complement(SFormsFastMapDirect map) {\n\n    for (int i = 2; i >= 0; i--) {\n      FastSparseSet<Integer>[] lstOwn = elements[i];\n\n      if (lstOwn.length == 0) {\n        continue;\n      }\n\n      FastSparseSet<Integer>[] lstExtern = map.elements[i];\n      int[] arrnext = next[i];\n\n      int pointer = 0;\n      do {\n        FastSparseSet<Integer> first = lstOwn[pointer];\n\n        if (first != null) {\n          if (pointer >= lstExtern.length) {\n            break;\n          }\n          FastSparseSet<Integer> second = lstExtern[pointer];\n\n          if (second != null) {\n            first.complement(second);\n            if (first.isEmpty()) {\n              lstOwn[pointer] = null;\n              size--;\n              changeNext(arrnext, pointer, pointer, arrnext[pointer]);\n            }\n          }\n        }\n\n        pointer = arrnext[pointer];\n      }\n      while (pointer != 0);\n    }\n  }\n\n  public void intersection(SFormsFastMapDirect map) {\n\n    for (int i = 2; i >= 0; i--) {\n      FastSparseSet<Integer>[] lstOwn = elements[i];\n\n      if (lstOwn.length == 0) {\n        continue;\n      }\n\n      FastSparseSet<Integer>[] lstExtern = map.elements[i];\n      int[] arrnext = next[i];\n\n      int pointer = 0;\n      do {\n        FastSparseSet<Integer> first = lstOwn[pointer];\n\n        if (first != null) {\n          FastSparseSet<Integer> second = null;\n          if (pointer < lstExtern.length) {\n            second = lstExtern[pointer];\n          }\n\n          if (second != null) {\n            first.intersection(second);\n          }\n\n          if (second == null || first.isEmpty()) {\n            lstOwn[pointer] = null;\n            size--;\n            changeNext(arrnext, pointer, pointer, arrnext[pointer]);\n          }\n        }\n\n        pointer = arrnext[pointer];\n      }\n      while (pointer != 0);\n    }\n  }\n\n  public void union(SFormsFastMapDirect map) {\n\n    for (int i = 2; i >= 0; i--) {\n      FastSparseSet<Integer>[] lstExtern = map.elements[i];\n\n      if (lstExtern.length == 0) {\n        continue;\n      }\n\n      FastSparseSet<Integer>[] lstOwn = elements[i];\n      int[] arrnext = next[i];\n      int[] arrnextExtern = map.next[i];\n\n      int pointer = 0;\n      do {\n        if (pointer >= lstOwn.length) {\n          lstOwn = ensureCapacity(i, lstExtern.length, true);\n          arrnext = next[i];\n        }\n\n        FastSparseSet<Integer> second = lstExtern[pointer];\n\n        if (second != null) {\n          FastSparseSet<Integer> first = lstOwn[pointer];\n\n          if (first == null) {\n            lstOwn[pointer] = second.getCopy();\n            size++;\n            changeNext(arrnext, pointer, arrnext[pointer], pointer);\n          }\n          else {\n            first.union(second);\n          }\n        }\n\n        pointer = arrnextExtern[pointer];\n      }\n      while (pointer != 0);\n    }\n  }\n\n  public String toString() {\n\n    StringBuilder buffer = new StringBuilder(\"{\");\n\n    List<Entry<Integer, FastSparseSet<Integer>>> lst = entryList();\n    if (lst != null) {\n      boolean first = true;\n      for (Entry<Integer, FastSparseSet<Integer>> entry : lst) {\n        if (!first) {\n          buffer.append(\", \");\n        }\n        else {\n          first = false;\n        }\n\n        Set<Integer> set = entry.getValue().toPlainSet();\n        buffer.append(entry.getKey()).append(\"={\").append(set.toString()).append(\"}\");\n      }\n    }\n\n    buffer.append(\"}\");\n    return buffer.toString();\n  }\n\n  public List<Entry<Integer, FastSparseSet<Integer>>> entryList() {\n    List<Entry<Integer, FastSparseSet<Integer>>> list = new ArrayList<>();\n\n    for (int i = 2; i >= 0; i--) {\n      int ikey = 0;\n      for (final FastSparseSet<Integer> ent : elements[i]) {\n        if (ent != null) {\n          final int key = i == 0 ? ikey : (i == 1 ? ikey + VarExprent.STACK_BASE : -ikey);\n\n          list.add(new Entry<>() {\n\n            private final Integer var = key;\n            private final FastSparseSet<Integer> val = ent;\n\n            @Override\n            public Integer getKey() {\n              return var;\n            }\n\n            @Override\n            public FastSparseSet<Integer> getValue() {\n              return val;\n            }\n\n            @Override\n            public FastSparseSet<Integer> setValue(FastSparseSet<Integer> newvalue) {\n              return null;\n            }\n          });\n        }\n\n        ikey++;\n      }\n    }\n\n    return list;\n  }\n\n  private FastSparseSet<Integer>[] ensureCapacity(int index, int size, boolean exact) {\n\n    FastSparseSet<Integer>[] arr = elements[index];\n    int[] arrnext = next[index];\n\n    int minsize = size;\n    if (!exact) {\n      minsize = 2 * arr.length / 3 + 1;\n      if (size > minsize) {\n        minsize = size;\n      }\n    }\n\n    @SuppressWarnings(\"unchecked\") FastSparseSet<Integer>[] arrnew = new FastSparseSet[minsize];\n    System.arraycopy(arr, 0, arrnew, 0, arr.length);\n\n    int[] arrnextnew = new int[minsize];\n    System.arraycopy(arrnext, 0, arrnextnew, 0, arrnext.length);\n\n    elements[index] = arrnew;\n    next[index] = arrnextnew;\n\n    return arrnew;\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/TextBuffer.java",
    "content": "// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.util;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\n\nimport java.util.*;\n\n/**\n * Allows to connect text with resulting lines\n *\n * @author egor\n */\n@SuppressWarnings(\"UnusedReturnValue\")\npublic class TextBuffer {\n  private final String myLineSeparator = DecompilerContext.getNewLineSeparator();\n  private final String myIndent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING);\n  private final StringBuilder myStringBuilder;\n  private Map<Integer, Integer> myLineToOffsetMapping = null;\n\n  public TextBuffer() {\n    myStringBuilder = new StringBuilder();\n  }\n\n  public TextBuffer(int size) {\n    myStringBuilder = new StringBuilder(size);\n  }\n\n  public TextBuffer(String text) {\n    myStringBuilder = new StringBuilder(text);\n  }\n\n  public TextBuffer append(String str) {\n    myStringBuilder.append(str);\n    return this;\n  }\n\n  public TextBuffer append(char ch) {\n    myStringBuilder.append(ch);\n    return this;\n  }\n\n  public TextBuffer append(int i) {\n    myStringBuilder.append(i);\n    return this;\n  }\n\n  public TextBuffer appendLineSeparator() {\n    myStringBuilder.append(myLineSeparator);\n    return this;\n  }\n\n  public TextBuffer appendIndent(int length) {\n    while (length-- > 0) {\n      append(myIndent);\n    }\n    return this;\n  }\n\n  public TextBuffer prepend(String s) {\n    myStringBuilder.insert(0, s);\n    shiftMapping(s.length());\n    return this;\n  }\n\n  public TextBuffer enclose(String left, String right) {\n    prepend(left);\n    append(right);\n    return this;\n  }\n\n  public boolean containsOnlyWhitespaces() {\n    for (int i = 0; i < myStringBuilder.length(); i++) {\n      if (myStringBuilder.charAt(i) != ' ') {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  @Override\n  public String toString() {\n    String original = myStringBuilder.toString();\n    if (myLineToOffsetMapping == null || myLineToOffsetMapping.isEmpty()) {\n      if (myLineMapping != null) {\n        return addOriginalLineNumbers();\n      }\n      return original;\n    }\n    else {\n      StringBuilder res = new StringBuilder();\n      String[] srcLines = original.split(myLineSeparator);\n      int currentLineStartOffset = 0;\n      int currentLine = 0;\n      int previousMarkLine = 0;\n      int dumpedLines = 0;\n      ArrayList<Integer> linesWithMarks = new ArrayList<>(myLineToOffsetMapping.keySet());\n      Collections.sort(linesWithMarks);\n      for (Integer markLine : linesWithMarks) {\n        Integer markOffset = myLineToOffsetMapping.get(markLine);\n        while (currentLine < srcLines.length) {\n          String line = srcLines[currentLine];\n          int lineEnd = currentLineStartOffset + line.length() + myLineSeparator.length();\n          if (markOffset <= lineEnd) {\n            int requiredLine = markLine - 1;\n            int linesToAdd = requiredLine - dumpedLines;\n            dumpedLines = requiredLine;\n            appendLines(res, srcLines, previousMarkLine, currentLine, linesToAdd);\n            previousMarkLine = currentLine;\n            break;\n          }\n          currentLineStartOffset = lineEnd;\n          currentLine++;\n        }\n      }\n      if (previousMarkLine < srcLines.length) {\n        appendLines(res, srcLines, previousMarkLine, srcLines.length, srcLines.length - previousMarkLine);\n      }\n\n      return res.toString();\n    }\n  }\n\n  private String addOriginalLineNumbers() {\n    StringBuilder sb = new StringBuilder();\n    int lineStart = 0, lineEnd;\n    int count = 0, length = myLineSeparator.length();\n    while ((lineEnd = myStringBuilder.indexOf(myLineSeparator, lineStart)) > 0) {\n      ++count;\n      sb.append(myStringBuilder, lineStart, lineEnd);\n      Set<Integer> integers = myLineMapping.get(count);\n      if (integers != null) {\n        sb.append(\"//\");\n        for (Integer integer : integers) {\n          sb.append(' ').append(integer);\n        }\n      }\n      sb.append(myLineSeparator);\n      lineStart = lineEnd + length;\n    }\n    if (lineStart < myStringBuilder.length()) {\n      sb.append(myStringBuilder.substring(lineStart));\n    }\n    return sb.toString();\n  }\n\n  private void appendLines(StringBuilder res, String[] srcLines, int from, int to, int requiredLineNumber) {\n    if (to - from > requiredLineNumber) {\n      List<String> strings = compactLines(Arrays.asList(srcLines).subList(from, to) ,requiredLineNumber);\n      int separatorsRequired = requiredLineNumber - 1;\n      for (String s : strings) {\n        res.append(s);\n        if (separatorsRequired-- > 0) {\n          res.append(myLineSeparator);\n        }\n      }\n      res.append(myLineSeparator);\n    }\n    else if (to - from <= requiredLineNumber) {\n      for (int i = from; i < to; i++) {\n        res.append(srcLines[i]).append(myLineSeparator);\n      }\n      for (int i = 0; i < requiredLineNumber - to + from; i++) {\n        res.append(myLineSeparator);\n      }\n    }\n  }\n\n  public int length() {\n    return myStringBuilder.length();\n  }\n\n  public void setStart(int position) {\n    myStringBuilder.delete(0, position);\n    shiftMapping(-position);\n  }\n\n  public void setLength(int position) {\n    myStringBuilder.setLength(position);\n    if (myLineToOffsetMapping != null) {\n      Map<Integer, Integer> newMap = new HashMap<>();\n      for (Map.Entry<Integer, Integer> entry : myLineToOffsetMapping.entrySet()) {\n        if (entry.getValue() <= position) {\n          newMap.put(entry.getKey(), entry.getValue());\n        }\n      }\n      myLineToOffsetMapping = newMap;\n    }\n  }\n\n  public TextBuffer append(TextBuffer buffer) {\n    if (buffer.myLineToOffsetMapping != null && !buffer.myLineToOffsetMapping.isEmpty()) {\n      checkMapCreated();\n      for (Map.Entry<Integer, Integer> entry : buffer.myLineToOffsetMapping.entrySet()) {\n        myLineToOffsetMapping.put(entry.getKey(), entry.getValue() + myStringBuilder.length());\n      }\n    }\n    myStringBuilder.append(buffer.myStringBuilder);\n    return this;\n  }\n\n  private void shiftMapping(int shiftOffset) {\n    if (myLineToOffsetMapping != null) {\n      Map<Integer, Integer> newMap = new HashMap<>();\n      for (Map.Entry<Integer, Integer> entry : myLineToOffsetMapping.entrySet()) {\n        int newValue = entry.getValue();\n        if (newValue >= 0) {\n          newValue += shiftOffset;\n        }\n        if (newValue >= 0) {\n          newMap.put(entry.getKey(), newValue);\n        }\n      }\n      myLineToOffsetMapping = newMap;\n    }\n  }\n\n  private void checkMapCreated() {\n    if (myLineToOffsetMapping == null) {\n      myLineToOffsetMapping = new HashMap<>();\n    }\n  }\n\n  public int countLines() {\n    return countLines(0);\n  }\n\n  public int countLines(int from) {\n    return count(myLineSeparator, from);\n  }\n\n  public int count(String substring, int from) {\n    int count = 0, length = substring.length(), p = from;\n    while ((p = myStringBuilder.indexOf(substring, p)) > 0) {\n      ++count;\n      p += length;\n    }\n    return count;\n  }\n\n  private static List<String> compactLines(List<String> srcLines, int requiredLineNumber) {\n    if (srcLines.size() < 2 || srcLines.size() <= requiredLineNumber) {\n      return srcLines;\n    }\n    List<String> res = new LinkedList<>(srcLines);\n    // first join lines with a single { or }\n    for (int i = res.size()-1; i > 0 ; i--) {\n      String s = res.get(i);\n      if (s.trim().equals(\"{\") || s.trim().equals(\"}\")) {\n        res.set(i-1, res.get(i-1).concat(s));\n        res.remove(i);\n      }\n      if (res.size() <= requiredLineNumber) {\n        return res;\n      }\n    }\n    // now join empty lines\n    for (int i = res.size()-1; i > 0 ; i--) {\n      String s = res.get(i);\n      if (s.trim().isEmpty()) {\n        res.set(i-1, res.get(i-1).concat(s));\n        res.remove(i);\n      }\n      if (res.size() <= requiredLineNumber) {\n        return res;\n      }\n    }\n    return res;\n  }\n\n  private Map<Integer, Set<Integer>> myLineMapping = null; // new to original\n\n  public void dumpOriginalLineNumbers(int[] lineMapping) {\n    if (lineMapping.length > 0) {\n      myLineMapping = new HashMap<>();\n      for (int i = 0; i < lineMapping.length; i += 2) {\n        int key = lineMapping[i + 1];\n        Set<Integer> existing = myLineMapping.computeIfAbsent(key, k -> new TreeSet<>());\n        existing.add(lineMapping[i]);\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/TextUtil.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler.util;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.ClassesProcessor;\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\n\npublic final class TextUtil {\n  private static final HashSet<String> KEYWORDS = new HashSet<>(Arrays.asList(\n    \"abstract\", \"default\", \"if\", \"private\", \"this\", \"boolean\", \"do\", \"implements\", \"protected\", \"throw\", \"break\", \"double\", \"import\",\n    \"public\", \"throws\", \"byte\", \"else\", \"instanceof\", \"return\", \"transient\", \"case\", \"extends\", \"int\", \"short\", \"try\", \"catch\", \"final\",\n    \"interface\", \"static\", \"void\", \"char\", \"finally\", \"long\", \"strictfp\", \"volatile\", \"class\", \"float\", \"native\", \"super\", \"while\",\n    \"const\", \"for\", \"new\", \"switch\", \"continue\", \"goto\", \"package\", \"synchronized\", \"true\", \"false\", \"null\", \"assert\"));\n\n  public static void writeQualifiedSuper(TextBuffer buf, String qualifier) {\n    ClassesProcessor.ClassNode classNode = (ClassesProcessor.ClassNode)DecompilerContext.getProperty(DecompilerContext.CURRENT_CLASS_NODE);\n    if (!qualifier.equals(classNode.classStruct.qualifiedName)) {\n      buf.append(DecompilerContext.getImportCollector().getNestedName(ExprProcessor.buildJavaClassName(qualifier))).append('.');\n    }\n    buf.append(\"super\");\n  }\n\n  public static String getIndentString(int length) {\n    if (length == 0) return \"\";\n    StringBuilder buf = new StringBuilder();\n    String indent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING);\n    append(buf, indent, length);\n    return buf.toString();\n  }\n\n  public static void append(StringBuilder buf, String string, int times) {\n    while (times-- > 0) buf.append(string);\n  }\n\n  public static boolean isPrintableUnicode(char c) {\n    int t = Character.getType(c);\n    return t != Character.UNASSIGNED && t != Character.LINE_SEPARATOR && t != Character.PARAGRAPH_SEPARATOR &&\n           t != Character.CONTROL && t != Character.FORMAT && t != Character.PRIVATE_USE && t != Character.SURROGATE;\n  }\n\n  public static String charToUnicodeLiteral(int value) {\n    String sTemp = Integer.toHexString(value);\n    sTemp = (\"0000\" + sTemp).substring(sTemp.length());\n    return \"\\\\u\" + sTemp;\n  }\n\n  public static boolean isValidIdentifier(String id, int version) {\n    return isJavaIdentifier(id) && !isKeyword(id, version);\n  }\n\n  private static boolean isJavaIdentifier(String id) {\n    if (id.isEmpty() || !Character.isJavaIdentifierStart(id.charAt(0))) {\n      return false;\n    }\n\n    for (int i = 1; i < id.length(); i++) {\n      if (!Character.isJavaIdentifierPart(id.charAt(i))) {\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  private static boolean isKeyword(String id, int version) {\n    return KEYWORDS.contains(id) || version >= CodeConstants.BYTECODE_JAVA_5 && \"enum\".equals(id);\n  }\n\n  public static String getInstructionName(int opcode) {\n    return opcodeNames[opcode];\n  }\n\n  private static final String[] opcodeNames = {\n    \"nop\",\n    \"aconst_null\",\n    \"iconst_m1\",\n    \"iconst_0\",\n    \"iconst_1\",\n    \"iconst_2\",\n    \"iconst_3\",\n    \"iconst_4\",\n    \"iconst_5\",\n    \"lconst_0\",\n    \"lconst_1\",\n    \"fconst_0\",\n    \"fconst_1\",\n    \"fconst_2\",\n    \"dconst_0\",\n    \"dconst_1\",\n    \"bipush\",\n    \"sipush\",\n    \"ldc\",\n    \"ldc_w\",\n    \"ldc2_w\",\n    \"iload\",\n    \"lload\",\n    \"fload\",\n    \"dload\",\n    \"aload\",\n    \"iload_0\",\n    \"iload_1\",\n    \"iload_2\",\n    \"iload_3\",\n    \"lload_0\",\n    \"lload_1\",\n    \"lload_2\",\n    \"lload_3\",\n    \"fload_0\",\n    \"fload_1\",\n    \"fload_2\",\n    \"fload_3\",\n    \"dload_0\",\n    \"dload_1\",\n    \"dload_2\",\n    \"dload_3\",\n    \"aload_0\",\n    \"aload_1\",\n    \"aload_2\",\n    \"aload_3\",\n    \"iaload\",\n    \"laload\",\n    \"faload\",\n    \"daload\",\n    \"aaload\",\n    \"baload\",\n    \"caload\",\n    \"saload\",\n    \"istore\",\n    \"lstore\",\n    \"fstore\",\n    \"dstore\",\n    \"astore\",\n    \"istore_0\",\n    \"istore_1\",\n    \"istore_2\",\n    \"istore_3\",\n    \"lstore_0\",\n    \"lstore_1\",\n    \"lstore_2\",\n    \"lstore_3\",\n    \"fstore_0\",\n    \"fstore_1\",\n    \"fstore_2\",\n    \"fstore_3\",\n    \"dstore_0\",\n    \"dstore_1\",\n    \"dstore_2\",\n    \"dstore_3\",\n    \"astore_0\",\n    \"astore_1\",\n    \"astore_2\",\n    \"astore_3\",\n    \"iastore\",\n    \"lastore\",\n    \"fastore\",\n    \"dastore\",\n    \"aastore\",\n    \"bastore\",\n    \"castore\",\n    \"sastore\",\n    \"pop\",\n    \"pop2\",\n    \"dup\",\n    \"dup_x1\",\n    \"dup_x2\",\n    \"dup2\",\n    \"dup2_x1\",\n    \"dup2_x2\",\n    \"swap\",\n    \"iadd\",\n    \"ladd\",\n    \"fadd\",\n    \"dadd\",\n    \"isub\",\n    \"lsub\",\n    \"fsub\",\n    \"dsub\",\n    \"imul\",\n    \"lmul\",\n    \"fmul\",\n    \"dmul\",\n    \"idiv\",\n    \"ldiv\",\n    \"fdiv\",\n    \"ddiv\",\n    \"irem\",\n    \"lrem\",\n    \"frem\",\n    \"drem\",\n    \"ineg\",\n    \"lneg\",\n    \"fneg\",\n    \"dneg\",\n    \"ishl\",\n    \"lshl\",\n    \"ishr\",\n    \"lshr\",\n    \"iushr\",\n    \"lushr\",\n    \"iand\",\n    \"land\",\n    \"ior\",\n    \"lor\",\n    \"ixor\",\n    \"lxor\",\n    \"iinc\",\n    \"i2l\",\n    \"i2f\",\n    \"i2d\",\n    \"l2i\",\n    \"l2f\",\n    \"l2d\",\n    \"f2i\",\n    \"f2l\",\n    \"f2d\",\n    \"d2i\",\n    \"d2l\",\n    \"d2f\",\n    \"i2b\",\n    \"i2c\",\n    \"i2s\",\n    \"lcmp\",\n    \"fcmpl\",\n    \"fcmpg\",\n    \"dcmpl\",\n    \"dcmpg\",\n    \"ifeq\",\n    \"ifne\",\n    \"iflt\",\n    \"ifge\",\n    \"ifgt\",\n    \"ifle\",\n    \"if_icmpeq\",\n    \"if_icmpne\",\n    \"if_icmplt\",\n    \"if_icmpge\",\n    \"if_icmpgt\",\n    \"if_icmple\",\n    \"if_acmpeq\",\n    \"if_acmpne\",\n    \"goto\",\n    \"jsr\",\n    \"ret\",\n    \"tableswitch\",\n    \"lookupswitch\",\n    \"ireturn\",\n    \"lreturn\",\n    \"freturn\",\n    \"dreturn\",\n    \"areturn\",\n    \"return\",\n    \"getstatic\",\n    \"putstatic\",\n    \"getfield\",\n    \"putfield\",\n    \"invokevirtual\",\n    \"invokespecial\",\n    \"invokestatic\",\n    \"invokeinterface\",\n    \"invokedynamic\",  // since Java 7\n    \"new\",\n    \"newarray\",\n    \"anewarray\",\n    \"arraylength\",\n    \"athrow\",\n    \"checkcast\",\n    \"instanceof\",\n    \"monitorenter\",\n    \"monitorexit\",\n    \"wide\",\n    \"multianewarray\",\n    \"ifnull\",\n    \"ifnonnull\",\n    \"goto_w\",\n    \"jsr_w\"\n  };\n}"
  },
  {
    "path": "src/org/jetbrains/java/decompiler/util/VBStyleCollection.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler.util;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\n\npublic class VBStyleCollection<E, K> extends ArrayList<E> {\n\n  private HashMap<K, Integer> map = new HashMap<>();\n\n  private ArrayList<K> lstKeys = new ArrayList<>();\n\n  public VBStyleCollection() {\n    super();\n  }\n\n  public VBStyleCollection(int initialCapacity) {\n    super(initialCapacity);\n    lstKeys = new ArrayList<>(initialCapacity);\n    map = new HashMap<>(initialCapacity);\n  }\n\n  @Override\n  public boolean add(E element) {\n    lstKeys.add(null);\n    super.add(element);\n    return true;\n  }\n\n  @Override\n  public boolean remove(Object element) {   // TODO: error on void remove(E element)\n    throw new RuntimeException(\"not implemented!\");\n  }\n\n  @Override\n  public boolean addAll(Collection<? extends E> c) {\n    for (int i = c.size() - 1; i >= 0; i--) {\n      lstKeys.add(null);\n    }\n    return super.addAll(c);\n  }\n\n  public void addAllWithKey(Collection<E> elements, Collection<K> keys) {\n    int index = super.size();\n\n    for (K key : keys) {\n      map.put(key, index++);\n    }\n\n    super.addAll(elements);\n    lstKeys.addAll(keys);\n  }\n\n  public void addWithKey(E element, K key) {\n    map.put(key, super.size());\n    super.add(element);\n    lstKeys.add(key);\n  }\n\n  // TODO: speed up the method\n  public E putWithKey(E element, K key) {\n    Integer index = map.get(key);\n    if (index == null) {\n      addWithKey(element, key);\n    }\n    else {\n      return super.set(index, element);\n    }\n    return null;\n  }\n\n  @Override\n  public void add(int index, E element) {\n    addToListIndex(index, 1);\n    lstKeys.add(index, null);\n    super.add(index, element);\n  }\n\n  public void addWithKeyAndIndex(int index, E element, K key) {\n    addToListIndex(index, 1);\n    map.put(key, index);\n    super.add(index, element);\n    lstKeys.add(index, key);\n  }\n\n  public void removeWithKey(K key) {\n    int index = map.get(key);\n    addToListIndex(index + 1, -1);\n    super.remove(index);\n    lstKeys.remove(index);\n    map.remove(key);\n  }\n\n  @Override\n  public E remove(int index) {\n    addToListIndex(index + 1, -1);\n    K obj = lstKeys.get(index);\n    if (obj != null) {\n      map.remove(obj);\n    }\n    lstKeys.remove(index);\n    return super.remove(index);\n  }\n\n  public E getWithKey(K key) {\n    Integer index = map.get(key);\n    if (index == null) {\n      return null;\n    }\n    return super.get(index);\n  }\n\n  public int getIndexByKey(K key) {\n    return map.get(key);\n  }\n\n  public E getLast() {\n    return super.get(super.size() - 1);\n  }\n\n  public boolean containsKey(K key) {\n    return map.containsKey(key);\n  }\n\n  @Override\n  public void clear() {\n    map.clear();\n    lstKeys.clear();\n    super.clear();\n  }\n\n  @Override\n  public VBStyleCollection<E, K> clone() {\n    VBStyleCollection<E, K> c = new VBStyleCollection<>();\n    c.addAll(new ArrayList<>(this));\n    c.setMap(new HashMap<>(map));\n    c.setLstKeys(new ArrayList<>(lstKeys));\n    return c;\n  }\n\n  public void setMap(HashMap<K, Integer> map) {\n    this.map = map;\n  }\n\n  public K getKey(int index) {\n    return lstKeys.get(index);\n  }\n\n  public ArrayList<K> getLstKeys() {\n    return lstKeys;\n  }\n\n  public void setLstKeys(ArrayList<K> lstKeys) {\n    this.lstKeys = lstKeys;\n  }\n\n  private void addToListIndex(int index, int diff) {\n    for (int i = lstKeys.size() - 1; i >= index; i--) {\n      K obj = lstKeys.get(i);\n      if (obj != null) {\n        map.put(obj, i + diff);\n      }\n    }\n  }\n}"
  },
  {
    "path": "test/org/jetbrains/java/decompiler/BulkDecompilationTest.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Map;\nimport java.util.zip.ZipFile;\n\nimport static org.jetbrains.java.decompiler.DecompilerTestFixture.assertFilesEqual;\n\npublic class BulkDecompilationTest {\n  private DecompilerTestFixture fixture;\n\n  @Before\n  public void setUp() throws IOException {\n    fixture = new DecompilerTestFixture();\n    fixture.setUp(Map.of());\n  }\n\n  @After\n  public void tearDown() throws IOException {\n    fixture.tearDown();\n    fixture = null;\n  }\n\n  @Test\n  public void testDirectory() {\n    var classes = fixture.getTempDir().resolve(\"classes\");\n    unpack(fixture.getTestDataDir().resolve(\"bulk.jar\"), classes);\n\n    var decompiler = fixture.getDecompiler();\n    decompiler.addSource(classes.toFile());\n    decompiler.decompileContext();\n\n    assertFilesEqual(fixture.getTestDataDir().resolve(\"bulk\"), fixture.getTargetDir());\n  }\n\n  @Test\n  public void testJar() {\n    doTestJar(\"bulk\");\n  }\n\n  @Test\n  public void testKtJar() {\n    doTestJar(\"kt25937\");\n  }\n\n  @Test\n  public void testObfuscated() {\n    doTestJar(\"obfuscated\");\n  }\n\n  private void doTestJar(String name) {\n    var decompiler = fixture.getDecompiler();\n    var jarName = name + \".jar\";\n    decompiler.addSource(fixture.getTestDataDir().resolve(jarName).toFile());\n    decompiler.decompileContext();\n\n    var unpacked = fixture.getTempDir().resolve(\"unpacked\");\n    unpack(fixture.getTargetDir().resolve(jarName), unpacked);\n\n    assertFilesEqual(fixture.getTestDataDir().resolve(name), unpacked);\n  }\n\n  private static void unpack(Path archive, Path targetDir) {\n    try (var zip = new ZipFile(archive.toFile())) {\n      var entries = zip.entries();\n      while (entries.hasMoreElements()) {\n        var entry = entries.nextElement();\n        if (!entry.isDirectory()) {\n          if (entry.getName().contains(\"..\")) throw new IllegalArgumentException(\"Invalid entry: \" + entry.getName());\n          var file = targetDir.resolve(entry.getName());\n          Files.createDirectories(file.getParent());\n          try (InputStream in = zip.getInputStream(entry)) {\n            Files.copy(in, file);\n          }\n        }\n      }\n    }\n    catch (IOException e) {\n      throw new UncheckedIOException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "test/org/jetbrains/java/decompiler/CancellableTest.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler;\n\nimport org.assertj.core.api.Assertions;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.Timeout;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\npublic class CancellableTest {\n  public static final int MIN_CALL_NUMBERS = 5;\n  private DecompilerTestFixture fixture;\n\n  /*\n   * Set individual test duration time limit to 60 seconds.\n   * This will help us to test bugs hanging decompiler.\n   */\n  @Rule\n  public Timeout globalTimeout = Timeout.seconds(60);\n\n  @Before\n  public void setUp() throws IOException {\n    fixture = new DecompilerTestFixture();\n    CancellationManager cancellationManager = new CancellationManager() {\n      private final AtomicInteger myAtomicInteger = new AtomicInteger(0);\n\n      @Override\n      public void checkCanceled() throws CanceledException {\n        check();\n      }\n\n      @Override\n      public void startMethod(String className, String methodName) {\n\n      }\n\n      @Override\n      public void finishMethod(String className, String methodName) {\n\n      }\n\n\n      private void check() {\n        if (myAtomicInteger.incrementAndGet() > MIN_CALL_NUMBERS) {\n          throw new CanceledException(new IllegalArgumentException());\n        }\n      }\n    };\n\n    fixture.setUp(Map.of(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, \"1\",\n                         IFernflowerPreferences.DUMP_ORIGINAL_LINES, \"1\",\n                         IFernflowerPreferences.IGNORE_INVALID_BYTECODE, \"1\",\n                         IFernflowerPreferences.VERIFY_ANONYMOUS_CLASSES, \"1\"),\n                  cancellationManager);\n  }\n\n  @After\n  public void tearDown() throws IOException {\n    fixture.tearDown();\n    fixture = null;\n  }\n\n  @Test\n  public void testCancellablePrimitiveNarrowing() {\n    doCancellableTest(\"pkg/TestPrimitiveNarrowing\");\n  }\n\n  @Test\n  public void testCancellableClassFields() { doCancellableTest(\"pkg/TestClassFields\"); }\n\n  @Test\n  public void testCancellableInterfaceFields() { doCancellableTest(\"pkg/TestInterfaceFields\"); }\n\n  @Test\n  public void testCancellableClassLambda() { doCancellableTest(\"pkg/TestClassLambda\"); }\n\n  private void doCancellableTest(String testFile, String... companionFiles) {\n    var decompiler = fixture.getDecompiler();\n\n    var classFile = fixture.getTestDataDir().resolve(\"classes/\" + testFile + \".class\");\n    assertThat(classFile).isRegularFile();\n    for (var file : SingleClassesTest.collectClasses(classFile)) {\n      decompiler.addSource(file.toFile());\n    }\n\n    for (String companionFile : companionFiles) {\n      var companionClassFile = fixture.getTestDataDir().resolve(\"classes/\" + companionFile + \".class\");\n      assertThat(companionClassFile).isRegularFile();\n      for (var file : SingleClassesTest.collectClasses(companionClassFile)) {\n        decompiler.addSource(file.toFile());\n      }\n    }\n    Assertions.assertThatThrownBy(() -> {\n        decompiler.decompileContext();\n      })\n      .isInstanceOf(CancellationManager.CanceledException.class)\n      .matches(exception -> exception.getCause() instanceof IllegalArgumentException);\n  }\n}\n"
  },
  {
    "path": "test/org/jetbrains/java/decompiler/ConverterHelperTest.java",
    "content": "// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.\npackage org.jetbrains.java.decompiler;\n\nimport org.jetbrains.java.decompiler.code.CodeConstants;\nimport org.jetbrains.java.decompiler.main.extern.IIdentifierRenamer.Type;\nimport org.jetbrains.java.decompiler.modules.renamer.ConverterHelper;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertTrue;\n\npublic class ConverterHelperTest {\n  private static final String VALID_CLASS_NAME = \"ValidClassName\";\n  private static final String VALID_FIELD_NAME = \"validFieldName\";\n  private static final String VALID_METHOD_NAME = \"validMethodName\";\n  private static final String VALID_FIELD_DESCRIPTOR = \"I\";\n  private static final String VALID_METHOD_DESCRIPTOR = \"()V\";\n\n  @Test public void testValidClassName() { doTestClassName(VALID_CLASS_NAME, false); }\n  @Test public void testValidFieldName() { doTestFieldName(VALID_FIELD_NAME, VALID_FIELD_DESCRIPTOR, false); }\n  @Test public void testValidMethodName() { doTestMethodName(VALID_METHOD_NAME, VALID_METHOD_DESCRIPTOR, false); }\n\n  @Test public void testNullClassName() { doTestClassName(null, true); }\n  @Test public void testNullFieldName() { doTestFieldName(null, VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testNullMethodName() { doTestMethodName(null, VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testEmptyClassName() { doTestClassName(\"\", true); }\n  @Test public void testEmptyFieldName() { doTestFieldName(\"\", VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testEmptyMethodName() { doTestMethodName(\"\", VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testShortClassName() { doTestClassName(\"C\", true); }\n  @Test public void testShortFieldName() { doTestFieldName(\"f\", VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testShortMethodName() { doTestMethodName(\"m\", VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testUnderscoreClassName() { doTestClassName(\"_\", true); }\n  @Test public void testUnderscoreFieldName() { doTestFieldName(\"_\", VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testUnderscoreMethodName() { doTestMethodName(\"_\", VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testKeywordClassName() { doTestClassName(\"public\", true); }\n  @Test public void testKeywordFieldName() { doTestFieldName(\"public\", VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testKeywordMethodName() { doTestMethodName(\"public\", VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testReservedWindowsNamespaceClassName() { doTestClassName(\"nul\", true); }\n  @Test public void testReservedWindowsNamespaceFieldName() { doTestFieldName(\"nul\", VALID_FIELD_DESCRIPTOR, false); }\n  @Test public void testReservedWindowsNamespaceName() { doTestMethodName(\"nul\", VALID_METHOD_DESCRIPTOR, false); }\n\n  @Test public void testLeadingDigitClassName() { doTestClassName(\"4identifier\", true); }\n  @Test public void testLeadingDigitFieldName() { doTestFieldName(\"4identifier\", VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testLeadingDigitMethodName() { doTestMethodName(\"4identifier\", VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testInvalidLeadingCharClassName() { doTestClassName(\"\\uFEFFClassName\", true); }\n  @Test public void testInvalidLeadingCharFieldName() { doTestFieldName(\"\\uFEFFfieldName\", VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testInvalidLeadingCharMethodName() { doTestMethodName(\"\\uFEFFmethodName\", VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testInvalidMiddleCharClassName() { doTestClassName(\"Class\\uFEFFName\", true); }\n  @Test public void testInvalidMiddleCharFieldName() { doTestFieldName(\"field\\uFEFFName\", VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testInvalidMiddleCharMethodName() { doTestMethodName(\"method\\uFEFFName\", VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testInvalidTrailingCharClassName() { doTestClassName(\"ClassName\\uFEFF\", true); }\n  @Test public void testInvalidTrailingCharFieldName() { doTestFieldName(\"fieldName\\uFEFF\", VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testInvalidTrailingCharMethodName() { doTestMethodName(\"methodName\\uFEFF\", VALID_METHOD_DESCRIPTOR, true); }\n\n  @Test public void testLtInitGtClassName() { doTestClassName(CodeConstants.INIT_NAME, true); }\n  @Test public void testLtInitGtFieldName() { doTestFieldName(CodeConstants.INIT_NAME, VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testLtInitGtMethodName() { doTestMethodName(CodeConstants.INIT_NAME, VALID_METHOD_DESCRIPTOR, false); }\n\n  @Test public void testLtClinitGtClassName() { doTestClassName(CodeConstants.CLINIT_NAME, true); }\n  @Test public void testLtClinitGtFieldName() { doTestFieldName(CodeConstants.CLINIT_NAME, VALID_FIELD_DESCRIPTOR, true); }\n  @Test public void testLtClinitGtMethodName() { doTestMethodName(CodeConstants.CLINIT_NAME, VALID_METHOD_DESCRIPTOR, false); }\n\n  private static void doTestClassName(String className, boolean shallBeRenamed) {\n    doTest(Type.ELEMENT_CLASS, className, null, null, shallBeRenamed);\n  }\n\n  private static void doTestFieldName(String element, String descriptor, boolean shallBeRenamed) {\n    doTest(Type.ELEMENT_FIELD, VALID_CLASS_NAME, element, descriptor, shallBeRenamed);\n  }\n\n  private static void doTestMethodName(String element, String descriptor, boolean shallBeRenamed) {\n    doTest(Type.ELEMENT_METHOD, VALID_CLASS_NAME, element, descriptor, shallBeRenamed);\n  }\n\n  private static void doTest(Type elementType, String className, String element, String descriptor, boolean shallBeRenamed) {\n    boolean result = new ConverterHelper().toBeRenamed(elementType, className, element, descriptor);\n    String assertionMessage = shallBeRenamed ? \"Identifier { %s, %s, %s, %s } shall be renamed\" : \"Identifier { %s, %s, %s, %s } shall not be renamed\";\n\n    assertTrue(String.format(assertionMessage, elementType.toString(), className, element, descriptor), result == shallBeRenamed);\n  }\n}"
  },
  {
    "path": "test/org/jetbrains/java/decompiler/DecompilerTestFixture.java",
    "content": "// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler;\n\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\nimport org.jetbrains.java.decompiler.main.CancellationManager;\nimport org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler;\nimport org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.jetbrains.java.decompiler.util.InterpreterUtil;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.Assert.assertTrue;\n\npublic class DecompilerTestFixture {\n  private Path testDataDir;\n  private Path tempDir;\n  private Path targetDir;\n  private TestConsoleDecompiler decompiler;\n\n  public void setUp(Map<String, String> customOptions) throws IOException {\n    setUp(customOptions, null);\n  }\n\n  public void setUp(@NotNull Map<String, String> customOptions,\n                    @Nullable CancellationManager cancellationManager) throws IOException {\n    testDataDir = Path.of(\"testData\");\n    if (!isTestDataDir(testDataDir)) testDataDir = Path.of(\"community/plugins/java-decompiler/engine/testData\");\n    if (!isTestDataDir(testDataDir)) testDataDir = Path.of(\"plugins/java-decompiler/engine/testData\");\n    if (!isTestDataDir(testDataDir)) testDataDir = Path.of(\"../community/plugins/java-decompiler/engine/testData\");\n    if (!isTestDataDir(testDataDir)) testDataDir = Path.of(\"../plugins/java-decompiler/engine/testData\");\n    assertTrue(\"cannot find the 'testData' directory relative to \" + Path.of(\"\").toAbsolutePath(), isTestDataDir(testDataDir));\n    testDataDir = testDataDir.toAbsolutePath();\n\n    tempDir = Files.createTempDirectory(\"decompiler_test_dir_\");\n\n    targetDir = Files.createDirectories(tempDir.resolve(\"decompiled\"));\n\n    Map<String, Object> options = new HashMap<>();\n    options.put(IFernflowerPreferences.LOG_LEVEL, \"warn\");\n    options.put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, \"1\");\n    options.put(IFernflowerPreferences.REMOVE_SYNTHETIC, \"1\");\n    options.put(IFernflowerPreferences.REMOVE_BRIDGE, \"1\");\n    options.put(IFernflowerPreferences.LITERALS_AS_IS, \"1\");\n    options.put(IFernflowerPreferences.UNIT_TEST_MODE, \"1\");\n    options.putAll(customOptions);\n\n    if (cancellationManager == null) {\n      decompiler = new TestConsoleDecompiler(targetDir.toFile(), options);\n    }\n    else {\n      decompiler = new TestConsoleDecompiler(targetDir.toFile(), options, cancellationManager);\n    }\n  }\n\n  public void tearDown() throws IOException {\n    try {\n      if (tempDir != null) {\n        deleteRecursively(tempDir);\n      }\n    }\n    finally {\n      decompiler.close();\n    }\n  }\n\n  public Path getTestDataDir() {\n    return testDataDir;\n  }\n\n  public Path getTempDir() {\n    return tempDir;\n  }\n\n  public Path getTargetDir() {\n    return targetDir;\n  }\n\n  public ConsoleDecompiler getDecompiler() {\n    return decompiler;\n  }\n\n  private static boolean isTestDataDir(Path dir) {\n    return Files.isDirectory(dir) && Files.isDirectory(dir.resolve(\"classes\")) && Files.isDirectory(dir.resolve(\"results\"));\n  }\n\n  private static void deleteRecursively(Path file) throws IOException {\n    Files.walkFileTree(file, new SimpleFileVisitor<>() {\n      @Override\n      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {\n        Files.delete(file);\n        return FileVisitResult.CONTINUE;\n      }\n\n      @Override\n      public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {\n        Files.delete(dir);\n        return FileVisitResult.CONTINUE;\n      }\n    });\n  }\n\n  public static void assertFilesEqual(Path expected, Path actual) {\n    if (Files.isDirectory(expected)) {\n      try {\n        Files.walkFileTree(expected, new SimpleFileVisitor<>() {\n          @Override\n          public FileVisitResult visitFile(Path expectedFile, BasicFileAttributes attrs) {\n            Path actualFile = actual.resolve(expected.relativize(expectedFile));\n            assertThat(actualFile).usingCharset(StandardCharsets.UTF_8).hasSameTextualContentAs(expectedFile, StandardCharsets.UTF_8);\n            return FileVisitResult.CONTINUE;\n          }\n        });\n      }\n      catch (IOException e) {\n        throw new UncheckedIOException(e);\n      }\n    }\n    else {\n      assertThat(actual).usingCharset(StandardCharsets.UTF_8).hasSameTextualContentAs(expected, StandardCharsets.UTF_8);\n    }\n  }\n\n  // cache zip files\n  private static class TestConsoleDecompiler extends ConsoleDecompiler {\n    private final Map<String, ZipFile> zipFiles = new HashMap<>();\n\n    TestConsoleDecompiler(File destination, Map<String, Object> options) {\n      super(destination, options, new PrintStreamLogger(System.out));\n    }\n\n    TestConsoleDecompiler(File destination, Map<String, Object> options, CancellationManager cancellationManager) {\n      super(destination, options, new PrintStreamLogger(System.out), cancellationManager);\n    }\n\n    @Override\n    public byte[] getBytecode(String externalPath, String internalPath) throws IOException {\n      File file = new File(externalPath);\n      if (internalPath == null) {\n        return InterpreterUtil.getBytes(file);\n      }\n      else {\n        ZipFile archive = zipFiles.get(file.getName());\n        if (archive == null) {\n          archive = new ZipFile(file);\n          zipFiles.put(file.getName(), archive);\n        }\n        ZipEntry entry = archive.getEntry(internalPath);\n        if (entry == null) throw new IOException(\"Entry not found: \" + internalPath);\n        return InterpreterUtil.getBytes(archive, entry);\n      }\n    }\n\n    void close() {\n      for (ZipFile file : zipFiles.values()) {\n        try {\n          file.close();\n        }\n        catch (IOException e) {\n          e.printStackTrace();\n        }\n      }\n      zipFiles.clear();\n    }\n  }\n}\n"
  },
  {
    "path": "test/org/jetbrains/java/decompiler/SingleClassesTest.java",
    "content": "// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.\npackage org.jetbrains.java.decompiler;\n\nimport org.jetbrains.java.decompiler.main.DecompilerContext;\nimport org.jetbrains.java.decompiler.main.extern.ClassFormatException;\nimport org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.Timeout;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.DirectoryStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.jetbrains.java.decompiler.DecompilerTestFixture.assertFilesEqual;\nimport static org.junit.Assert.assertTrue;\n\npublic class SingleClassesTest {\n  private DecompilerTestFixture fixture;\n\n  /*\n   * Set individual test duration time limit to 60 seconds.\n   * This will help us to test bugs hanging decompiler.\n   */\n  @Rule\n  public Timeout globalTimeout = Timeout.seconds(60);\n\n  @Before\n  public void setUp() throws IOException {\n    fixture = new DecompilerTestFixture();\n    fixture.setUp(Map.of(IFernflowerPreferences.BYTECODE_SOURCE_MAPPING, \"1\",\n                         IFernflowerPreferences.DUMP_ORIGINAL_LINES, \"1\",\n                         IFernflowerPreferences.IGNORE_INVALID_BYTECODE, \"1\",\n                         IFernflowerPreferences.VERIFY_ANONYMOUS_CLASSES, \"1\",\n                         IFernflowerPreferences.CONVERT_PATTERN_SWITCH, \"1\",\n                         IFernflowerPreferences.CONVERT_RECORD_PATTERN, \"1\"\n    ));\n  }\n\n  @After\n  public void tearDown() throws IOException {\n    fixture.tearDown();\n    fixture = null;\n  }\n\n  @Test public void testPrimitiveNarrowing() { doTest(\"pkg/TestPrimitiveNarrowing\"); }\n  @Test public void testClassFields() { doTest(\"pkg/TestClassFields\"); }\n  @Test public void testInterfaceFields() { doTest(\"pkg/TestInterfaceFields\"); }\n  @Test public void testClassLambda() { doTest(\"pkg/TestClassLambda\"); }\n  @Test public void testClassLoop() { doTest(\"pkg/TestClassLoop\"); }\n  @Test public void testClassSwitch() { doTest(\"pkg/TestClassSwitch\"); }\n  @Test public void testClassTypes() { doTest(\"pkg/TestClassTypes\"); }\n  @Test public void testClassVar() { doTest(\"pkg/TestClassVar\"); }\n  @Test public void testClassNestedInitializer() { doTest(\"pkg/TestClassNestedInitializer\"); }\n  @Test public void testClassCast() { doTest(\"pkg/TestClassCast\"); }\n  @Test public void testDeprecations() { doTest(\"pkg/TestDeprecations\"); }\n  @Test public void testExtendsList() { doTest(\"pkg/TestExtendsList\"); }\n  @Test public void testMethodParameters() { doTest(\"pkg/TestMethodParameters\"); }\n  @Test public void testMethodParametersAttr() { doTest(\"pkg/TestMethodParametersAttr\"); }\n  @Test public void testCodeConstructs() { doTest(\"pkg/TestCodeConstructs\"); }\n  @Test public void testConstantsAsIs() { doTest(\"pkg/TestConstantsAsIs\"); }\n  @Test public void testConstants() {\n    DecompilerContext.setProperty(IFernflowerPreferences.LITERALS_AS_IS, \"0\");\n    doTest(\"pkg/TestConstants\");\n  }\n  @Test public void testInteger() {\n    DecompilerContext.setProperty(IFernflowerPreferences.LITERALS_AS_IS, \"0\");\n    doTest(\"java/lang/Integer\");\n  }\n  @Test public void testEnum() { doTest(\"pkg/TestEnum\"); }\n  @Test public void testDebugSymbols() { doTest(\"pkg/TestDebugSymbols\"); }\n  @Test public void testInvalidMethodSignature() { doTest(\"InvalidMethodSignature\"); }\n  @Test public void testAnonymousClassConstructor() { doTest(\"pkg/TestAnonymousClassConstructor\"); }\n  @Test public void testInnerClassConstructor() { doTest(\"pkg/TestInnerClassConstructor\"); }\n  @Test public void testInnerClassConstructor11() { doTest(\"v11/TestInnerClassConstructor\"); }\n  @Test public void testTryCatchFinally() { doTest(\"pkg/TestTryCatchFinally\"); }\n  @Test public void testTryCatchFinallyJsrRet() { doTest(\"pkg/TestTryCatchFinallyJsrRet\"); }\n  @Test public void testAmbiguousCall() { doTest(\"pkg/TestAmbiguousCall\"); }\n  @Test public void testAmbiguousCallWithDebugInfo() { doTest(\"pkg/TestAmbiguousCallWithDebugInfo\"); }\n  @Test public void testSimpleBytecodeMapping() { doTest(\"pkg/TestClassSimpleBytecodeMapping\"); }\n  @Test public void testSynchronizedMapping() { doTest(\"pkg/TestSynchronizedMapping\"); }\n  @Test public void testAbstractMethods() { doTest(\"pkg/TestAbstractMethods\"); }\n  @Test public void testLocalClass() { doTest(\"pkg/TestLocalClass\"); }\n  @Test public void testAnonymousClass() { doTest(\"pkg/TestAnonymousClass\"); }\n  @Test public void testThrowException() { doTest(\"pkg/TestThrowException\"); }\n  @Test public void testInnerLocal() { doTest(\"pkg/TestInnerLocal\"); }\n  @Test public void testInnerSignature() { doTest(\"pkg/TestInnerSignature\"); }\n  @Test public void testAnonymousSignature() { doTest(\"pkg/TestAnonymousSignature\"); }\n  @Test public void testLocalsSignature() { doTest(\"pkg/TestLocalsSignature\"); }\n  @Test public void testParameterizedTypes() { doTest(\"pkg/TestParameterizedTypes\"); }\n  @Test public void testShadowing() { doTest(\"pkg/TestShadowing\", \"pkg/Shadow\", \"ext/Shadow\", \"pkg/TestShadowingSuperClass\"); }\n  @Test public void testStringConcat() { doTest(\"pkg/TestStringConcat\"); }\n  @Test public void testJava9StringConcat() { doTest(\"java9/TestJava9StringConcat\"); }\n  @Test public void testJava9ModuleInfo() { doTest(\"java9/module-info\"); }\n  @Test public void testJava9PrivateInterfaceMethod() { doTest(\"java9/TestJava9PrivateInterfaceMethod\"); }\n  @Test public void testJava11StringConcat() { doTest(\"java11/TestJava11StringConcat\"); }\n  @Test public void testJava11StringConcatEmptyAffix() { doTest(\"java11/TestJava11StringConcatEmptyAffix\"); }\n  @Test public void testJava11StringConcatSpecialChars() { doTest(\"java11/TestJava11StringConcatSpecialChars\"); }\n  @Test public void testMethodReferenceSameName() { doTest(\"pkg/TestMethodReferenceSameName\"); }\n  @Test public void testMethodReferenceLetterClass() { doTest(\"pkg/TestMethodReferenceLetterClass\"); }\n  @Test public void testConstructorReference() { doTest(\"pkg/TestConstructorReference\"); }\n  @Test public void testMemberAnnotations() { doTest(\"pkg/TestMemberAnnotations\"); }\n  @Test public void testMoreAnnotations() { doTest(\"pkg/MoreAnnotations\"); }\n  @Test public void testStaticNameClash() { doTest(\"pkg/TestStaticNameClash\"); }\n  @Test public void testExtendingSubclass() { doTest(\"pkg/TestExtendingSubclass\"); }\n  @Test public void testSyntheticAccess() { doTest(\"pkg/TestSyntheticAccess\"); }\n  @Test public void testIllegalVarName() { doTest(\"pkg/TestIllegalVarName\"); }\n  @Test public void testIffSimplification() { doTest(\"pkg/TestIffSimplification\"); }\n  @Test public void testKotlinConstructor() { doTest(\"pkg/TestKotlinConstructorKt\"); }\n  @Test public void testAsserts() { doTest(\"pkg/TestAsserts\"); }\n  @Test public void testLocalsNames() { doTest(\"pkg/TestLocalsNames\"); }\n  @Test public void testAnonymousParamNames() { doTest(\"pkg/TestAnonymousParamNames\"); }\n  @Test public void testAnonymousParams() { doTest(\"pkg/TestAnonymousParams\"); }\n  @Test public void testAccessReplace() { doTest(\"pkg/TestAccessReplace\"); }\n  @Test public void testStringLiterals() { doTest(\"pkg/TestStringLiterals\"); }\n  @Test public void testPrimitives() { doTest(\"pkg/TestPrimitives\"); }\n  @Test public void testClashName() { doTest(\"pkg/TestClashName\", \"pkg/SharedName1\",\n          \"pkg/SharedName2\", \"pkg/SharedName3\", \"pkg/SharedName4\", \"pkg/NonSharedName\",\n          \"pkg/TestClashNameParent\", \"ext/TestClashNameParent\",\"pkg/TestClashNameIface\", \"ext/TestClashNameIface\"); }\n  @Test public void testSwitchOnEnum() { doTest(\"pkg/TestSwitchOnEnum\");}\n  @Test public void testSwitchOnEnumEclipse() { doTest(\"pkg/TestSwitchOnEnumEclipse\"); }\n  @Test public void testVarArgCalls() { doTest(\"pkg/TestVarArgCalls\"); }\n  @Test public void testLambdaParams() { doTest(\"pkg/TestLambdaParams\"); }\n  @Test public void testInterfaceMethods() { doTest(\"pkg/TestInterfaceMethods\"); }\n  @Test public void testConstType() { doTest(\"pkg/TestConstType\"); }\n  @Test public void testPop2OneDoublePop2() { doTest(\"pkg/TestPop2OneDoublePop2\"); }\n  @Test public void testPop2OneLongPop2() { doTest(\"pkg/TestPop2OneLongPop2\"); }\n  @Test public void testPop2TwoIntPop2() { doTest(\"pkg/TestPop2TwoIntPop2\"); }\n  @Test public void testPop2TwoIntTwoPop() { doTest(\"pkg/TestPop2TwoIntTwoPop\"); }\n  @Test public void testSuperInner() { doTest(\"pkg/TestSuperInner\", \"pkg/TestSuperInnerBase\"); }\n  @Test public void testMissingConstructorCallGood() { doTest(\"pkg/TestMissingConstructorCallGood\"); }\n  @Test public void testMissingConstructorCallBad() { doTest(\"pkg/TestMissingConstructorCallBad\"); }\n  @Test public void testEmptyBlocks() { doTest(\"pkg/TestEmptyBlocks\"); }\n  @Test public void testInvertedFloatComparison() { doTest(\"pkg/TestInvertedFloatComparison\"); }\n  @Test public void testPrivateEmptyConstructor() { doTest(\"pkg/TestPrivateEmptyConstructor\"); }\n  @Test public void testSynchronizedUnprotected() { doTest(\"pkg/TestSynchronizedUnprotected\"); }\n  @Test public void testInterfaceSuper() { doTest(\"pkg/TestInterfaceSuper\"); }\n  @Test public void testFieldSingleAccess() { doTest(\"pkg/TestFieldSingleAccess\"); }\n  @Test public void testPackageInfo() { doTest(\"pkg/package-info\"); }\n  @Test public void testIntVarMerge() { doTest(\"pkg/TestIntVarMerge\"); }\n  @Test public void testSwitchOnStringsJavac() { doTest(\"pkg/TestSwitchOnStringsJavac\"); }\n  @Test public void testSwitchOnStringsEcj() { doTest(\"pkg/TestSwitchOnStringsEcj\"); }\n  @Test public void testSwitchRules() { doTest(\"pkg/TestSwitchRules\"); }\n  @Test public void testSwitchSimpleReferencesJavac() { doTest(\"pkg/TestSwitchSimpleReferencesJavac\"); }\n  @Test public void testSwitchClassReferencesJavac() { doTest(\"pkg/TestSwitchClassReferencesJavac\"); }\n  @Test public void testSwitchClassReferencesEcj() { doTest(\"pkg/TestSwitchClassReferencesEcj\"); }\n  @Test public void testSwitchClassReferencesFastExitJavac() { doTest(\"pkg/TestSwitchClassReferencesFastExitJavac\"); }\n  @Test public void testSwitchClassReferencesFastExitEcj() { doTest(\"pkg/TestSwitchClassReferencesFastExitEcj\"); }\n  @Test public void testSwitchGuardedJavac() { doTest(\"pkg/TestSwitchGuardedJavac\"); }\n  @Test public void testSwitchGuarded2Javac() { doTest(\"pkg/TestSwitchGuarded2Javac\"); }\n  @Test public void testSwitchGuardedEcj() { doTest(\"pkg/TestSwitchGuardedEcj\"); }\n\n  //ecj doesn't support here, because it produces code with unnecessary assignments,\n  //which can confuse decompiler with ordinary ones\n  @Test public void testSimpleInstanceOfRecordPatternJavac() { doTest(\"pkg/TestSimpleInstanceOfRecordPatternJavac\"); }\n  @Test public void testComplexInstanceOfRecordPatternJavac() { doTest(\"pkg/TestComplexInstanceOfRecordPatternJavac\"); }\n  @Test public void testSwitchWithDeconstructionsWithoutNestedJavac() { doTest(\"pkg/TestSwitchWithDeconstructionsWithoutNestedJavac\"); }\n  @Test public void testSwitchNestedDeconstructionJavac() { doTest(\"pkg/TestSwitchNestedDeconstructionsJavac\"); }\n\n  // TODO: fix all below\n  //@Test public void testUnionType() { doTest(\"pkg/TestUnionType\"); }\n  //@Test public void testInnerClassConstructor2() { doTest(\"pkg/TestInner2\"); }\n  //@Test public void testInUse() { doTest(\"pkg/TestInUse\"); }\n  @Test public void testGroovyClass() { doTest(\"pkg/TestGroovyClass\"); }\n  @Test public void testGroovyTrait() { doTest(\"pkg/TestGroovyTrait\"); }\n  @Test public void testPrivateClasses() { doTest(\"pkg/PrivateClasses\"); }\n  @Test public void testSuspendLambda() { doTest(\"pkg/TestSuspendLambdaKt\"); }\n  @Test public void testNamedSuspendFun2Kt() { doTest(\"pkg/TestNamedSuspendFun2Kt\"); }\n  @Test public void testGenericArgs() { doTest(\"pkg/TestGenericArgs\"); }\n  @Test public void testRecordEmpty() { doTest(\"records/TestRecordEmpty\"); }\n  @Test public void testRecordSimple() { doTest(\"records/TestRecordSimple\"); }\n  @Test public void testRecordVararg() { doTest(\"records/TestRecordVararg\"); }\n  @Test public void testRecordGenericVararg() { doTest(\"records/TestRecordGenericVararg\"); }\n  @Test public void testRecordAnno() { doTest(\"records/TestRecordAnno\"); }\n  @Test public void testRootWithClassInner() { doTest(\"sealed/RootWithClassInner\"); }\n  @Test public void testRootWithInterfaceInner() { doTest(\"sealed/RootWithInterfaceInner\"); }\n  @Test public void testRootWithClassOuter() { doTest(\"sealed/RootWithClassOuter\",\n    \"sealed/ClassExtends\", \"sealed/ClassNonSealed\", \"sealed/ClassNonSealedExtendsImplements\");\n  }\n  @Test public void testRootWithClassOuterUnresolvable() { doTest(\"sealed/RootWithClassOuter\"); }\n  @Test public void testRootWithInterfaceOuter() { doTest(\"sealed/RootWithInterfaceOuter\",\n    \"sealed/ClassImplements\", \"sealed/InterfaceNonSealed\", \"sealed/ClassNonSealedExtendsImplements\");\n  }\n  @Test public void testClassNonSealed() { doTest(\"sealed/ClassNonSealed\",\n    \"sealed/RootWithClassOuter\", \"sealed/ClassExtends\", \"sealed/ClassNonSealedExtendsImplements\");\n  }\n  @Test public void testClassNonSealedExtendsImplements() { doTest(\"sealed/ClassNonSealedExtendsImplements\",\n    \"sealed/RootWithClassOuter\", \"sealed/ClassExtends\", \"sealed/ClassNonSealed\");\n  }\n  @Test public void testInterfaceNonSealed() { doTest(\"sealed/InterfaceNonSealed\",\n    \"sealed/RootWithInterfaceOuter\", \"sealed/ClassImplements\", \"sealed/ClassNonSealedExtendsImplements\");\n  }\n  @Test public void testRootWithModule() { doTest(\"sealed/foo/RootWithModule\", \"sealed/bar/BarClassExtends\");}\n  @Test public void testRootWithInterfaceInnerAndOuter() { doTest(\"sealed/RootWithInterfaceInnerAndOuter\", \"sealed/ClassNonSealed\");}\n  @Test public void testEnumWithOverride() { doTest(\"sealed/EnumWithOverride\");}\n  @Test public void testArrayTypeAnnotations() { doTest(\"typeAnnotations/ArrayTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/C\", \"typeAnnotations/D\");\n  }\n  @Test public void testGenericTypeAnnotations() { doTest(\"typeAnnotations/GenericTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/C\", \"typeAnnotations/D\", \"typeAnnotations/E\");\n  }\n  @Test public void testGenericArrayTypeAnnotations() {doTest(\"typeAnnotations/GenericArrayTypeAnnotations\",\n      \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/C\", \"typeAnnotations/D\", \"typeAnnotations/E\", \"typeAnnotations/F\");\n  }\n  @Test public void testNestedTypeAnnotations() {doTest(\"typeAnnotations/NestedTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/C\", \"typeAnnotations/D\", \"typeAnnotations/E\",\n    \"typeAnnotations/F\", \"typeAnnotations/Z\", \"typeAnnotations/P\", \"typeAnnotations/S\", \"typeAnnotations/T\");\n  }\n  @Test public void testArrayNestedTypeAnnotations() {doTest(\"typeAnnotations/ArrayNestedTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/C\", \"typeAnnotations/D\", \"typeAnnotations/Z\");\n  }\n  @Test public void testGenericNestedTypeAnnotations() {doTest(\"typeAnnotations/GenericNestedTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/C\", \"typeAnnotations/D\", \"typeAnnotations/E\",\n    \"typeAnnotations/V\");\n  }\n  @Test public void testGenericArrayNestedTypeAnnotations() {doTest(\"typeAnnotations/GenericArrayNestedTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/C\", \"typeAnnotations/D\", \"typeAnnotations/E\",\n    \"typeAnnotations/F\", \"typeAnnotations/V\");\n  }\n  @Test public void testClassSuperTypeAnnotations() {doTest(\"typeAnnotations/ClassSuperTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/F\");\n  }\n  @Test public void testInterfaceSuperTypeAnnotations() {doTest(\"typeAnnotations/InterfaceSuperTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/F\");\n  }\n  @Test public void testMemberDeclarationTypeAnnotations() {doTest(\"typeAnnotations/MemberDeclarationTypeAnnotations\",\n    \"typeAnnotations/A\", \"typeAnnotations/B\", \"typeAnnotations/C\", \"typeAnnotations/D\", \"typeAnnotations/E\",\n                                                                   \"typeAnnotations/K\", \"typeAnnotations/L\");\n  }\n  @Test public void testNestedType() { doTest(\"pkg/NestedType\"); }\n  @Test public void testInheritanceChainCycle() { doTest(\"pkg/TestInheritanceChainCycle\"); }\n  @Test public void testDynamicConstantPoolEntry() { doTest(\"java11/TestDynamicConstantPoolEntry\"); }\n  @Test public void testInstanceofWithPattern() {\n    doTest(\"patterns/TestInstanceofWithPattern\");\n  }\n  @Test public void testInstanceofVarNotSupported() {\n    // the bytecode version of this test data doesn't support patterns in `instanceof`, so no modifications regarding that are applied\n    doTest(\"patterns/TestInstanceofPatternNotSupported\");\n  }\n\n  @Test(expected = ClassFormatException.class)\n  public void testUnsupportedConstantPoolEntry() { doTest(\"java11/TestUnsupportedConstantPoolEntry\"); }\n\n  @Test public void testSwitchOnStatic() { doTest(\"pkg/SwitchOnStatic\"); }\n\n  @Test public void testTryToPreserveCast() { doTest(\"pkg/TryToPreserveCast\"); }\n\n  private void doTest(String testFile, String... companionFiles) {\n    var decompiler = fixture.getDecompiler();\n\n    var classFile = fixture.getTestDataDir().resolve(\"classes/\" + testFile + \".class\");\n    assertThat(classFile).isRegularFile();\n    for (var file : collectClasses(classFile)) {\n      decompiler.addSource(file.toFile());\n    }\n\n    for (String companionFile : companionFiles) {\n      var companionClassFile = fixture.getTestDataDir().resolve(\"classes/\" + companionFile + \".class\");\n      assertThat(companionClassFile).isRegularFile();\n      for (var file : collectClasses(companionClassFile)) {\n        decompiler.addSource(file.toFile());\n      }\n    }\n\n    decompiler.decompileContext();\n\n    var decompiledFile = fixture.getTargetDir().resolve(classFile.getFileName().toString().replace(\".class\", \".java\"));\n    assertThat(decompiledFile).isRegularFile();\n    assertTrue(Files.isRegularFile(decompiledFile));\n    var referenceFile = fixture.getTestDataDir().resolve(\"results/\" + classFile.getFileName().toString().replace(\".class\", \".dec\"));\n    assertThat(referenceFile).isRegularFile();\n    assertFilesEqual(referenceFile, decompiledFile);\n  }\n\n  static List<Path> collectClasses(Path classFile) {\n    var files = new ArrayList<Path>();\n    files.add(classFile);\n\n    var parent = classFile.getParent();\n    if (parent != null) {\n      var glob = classFile.getFileName().toString().replace(\".class\", \"$*.class\");\n      try (DirectoryStream<Path> inner = Files.newDirectoryStream(parent, glob)) {\n        inner.forEach(files::add);\n      }\n      catch (IOException e) {\n        throw new UncheckedIOException(e);\n      }\n    }\n\n    return files;\n  }\n}\n"
  },
  {
    "path": "testData/bulk/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nCreated-By: 1.8.0_20 (Oracle Corporation)\n\n"
  },
  {
    "path": "testData/bulk/pkg/Main.java",
    "content": "package pkg;\n\nimport pkg.res.Loader;\n\npublic class Main {\n   public static void main(String[] args) {\n      Loader loader = new Loader();\n      String resource = loader.getResource();\n      System.out.println(resource);\n   }\n}\n"
  },
  {
    "path": "testData/bulk/pkg/res/Loader.java",
    "content": "package pkg.res;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.net.URL;\n\npublic class Loader {\n   public String getResource() {\n      URL resource = this.getClass().getClassLoader().getResource(\"pkg/res/resource.txt\");\n      if (resource == null) {\n         throw new RuntimeException(\"Resource missing\");\n      } else {\n         try {\n            File file = new File(resource.toURI());\n            byte[] bytes = new byte[(int)file.length()];\n            FileInputStream stream = new FileInputStream(file);\n            stream.read(bytes);\n            stream.close();\n            return new String(bytes, \"UTF-8\");\n         } catch (Exception var5) {\n            Exception e = var5;\n            throw new RuntimeException(\"Resource load failed\", e);\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/bulk/pkg/res/resource.txt",
    "content": ""
  },
  {
    "path": "testData/classes/pkg/PrivateClasses.java",
    "content": "package pkg;\n\nclass PrivateClasses {\n  private interface Callable<T> {\n    T call();\n  }\n\n  private static final Runnable R1 = new Runnable() {\n    @Override\n    public void run() {\n      String s = \"\";\n\n      class NonCapturingLocalR1 {\n        private final String s;\n\n        public NonCapturingLocalR1(String s) {\n          this.s = s;\n        }\n\n        @Override\n        public String toString() {\n          return this.s;\n        }\n      }\n\n      class CapturingLocalR1 {\n        private final int i;\n\n        public CapturingLocalR1(int i) {\n          this.i = i;\n        }\n\n        @Override\n        public String toString() {\n          return s + \":\" + i;\n        }\n      }\n\n      new NonCapturingLocalR1(s).toString();\n      new CapturingLocalR1(42).toString();\n\n      Callable<String> c1 = new Callable<String>() {\n        @Override\n        public String call() {\n          return null;\n        }\n      };\n\n      Callable<String> c2 = new Callable<String>() {\n        @Override\n        public String call() {\n          return s;\n        }\n      };\n\n      (c1.call() + c2.call()).length();\n    }\n  };\n\n  private final Runnable R2 = new Runnable() {\n    @Override\n    public void run() {\n      String s = \"\";\n\n      class NonCapturingLocalR2 {\n        private final String s;\n\n        public NonCapturingLocalR2(String s) {\n          this.s = s;\n        }\n\n        @Override\n        public String toString() {\n          return this.s;\n        }\n      }\n\n      class CapturingLocalR1 {\n        private final int i;\n\n        public CapturingLocalR1(int i) {\n          this.i = i;\n        }\n\n        @Override\n        public String toString() {\n          return s + \":\" + i;\n        }\n      }\n\n      new NonCapturingLocalR2(s).toString();\n      new CapturingLocalR1(42).toString();\n\n      Callable<String> c1 = new Callable<String>() {\n        @Override\n        public String call() {\n          return null;\n        }\n      };\n\n      Callable<String> c2 = new Callable<String>() {\n        @Override\n        public String call() {\n          return s;\n        }\n      };\n\n      (c1.call() + c2.call()).length();\n    }\n  };\n\n  public static void m1(String s) {\n    class NonCapturingLocalM1 {\n      private final String s;\n\n      public NonCapturingLocalM1(String s) {\n        this.s = s;\n      }\n\n      @Override\n      public String toString() {\n        return this.s;\n      }\n    }\n\n    class CapturingLocalM1 {\n      private final int i;\n\n      public CapturingLocalM1(int i) {\n        this.i = i;\n      }\n\n      @Override\n      public String toString() {\n        return s + \":\" + i;\n      }\n    }\n\n    new NonCapturingLocalM1(s).toString();\n    new CapturingLocalM1(42).toString();\n\n    Callable<String> c1 = new Callable<String>() {\n      @Override\n      public String call() {\n        return null;\n      }\n    };\n\n    Callable<String> c2 = new Callable<String>() {\n      @Override\n      public String call() {\n        return s;\n      }\n    };\n\n    (c1.call() + c2.call()).length();\n  }\n\n  public void m2(String s) {\n    class NonCapturingLocalM2 {\n      private final String s;\n\n      public NonCapturingLocalM2(String s) {\n        this.s = s;\n      }\n\n      @Override\n      public String toString() {\n        return this.s;\n      }\n    }\n\n    class CapturingLocalM2 {\n      private final int i;\n\n      public CapturingLocalM2(int i) {\n        this.i = i;\n      }\n\n      @Override\n      public String toString() {\n        return s + \":\" + i;\n      }\n    }\n\n    new NonCapturingLocalM2(s).toString();\n    new CapturingLocalM2(42).toString();\n\n    Callable<String> c1 = new Callable<String>() {\n      @Override\n      public String call() {\n        return null;\n      }\n    };\n\n    Callable<String> c2 = new Callable<String>() {\n      @Override\n      public String call() {\n        return s;\n      }\n    };\n\n    (c1.call() + c2.call()).length();\n  }\n}"
  },
  {
    "path": "testData/kt25937/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\nCreated-By: 1.8.0_171 (Oracle Corporation)\n\n"
  },
  {
    "path": "testData/kt25937/kt/Kt25937Kt.java",
    "content": "package kt;\n\nimport kotlin.Metadata;\nimport kotlin.Unit;\nimport kotlin.coroutines.Continuation;\nimport kotlin.jvm.functions.Function1;\nimport kotlin.jvm.internal.Intrinsics;\nimport org.jetbrains.annotations.NotNull;\n\n@Metadata(\n   mv = {1, 1, 16},\n   bv = {1, 0, 3},\n   k = 2,\n   d1 = {\"\\u0000\\u001c\\n\\u0000\\n\\u0002\\u0010\\b\\n\\u0000\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0003\\u001a,\\u0010\\u0000\\u001a\\u00020\\u00012\\u001c\\u0010\\u0002\\u001a\\u0018\\b\\u0001\\u0012\\n\\u0012\\b\\u0012\\u0004\\u0012\\u00020\\u00050\\u0004\\u0012\\u0006\\u0012\\u0004\\u0018\\u00010\\u00060\\u0003ø\\u0001\\u0000¢\\u0006\\u0002\\u0010\\u0007\\u001a\\u0006\\u0010\\b\\u001a\\u00020\\u0001\\u0082\\u0002\\u0004\\n\\u0002\\b\\u0019¨\\u0006\\t\"},\n   d2 = {\"callSuspendBlock\", \"\", \"block\", \"Lkotlin/Function1;\", \"Lkotlin/coroutines/Continuation;\", \"\", \"\", \"(Lkotlin/jvm/functions/Function1;)I\", \"callSuspendBlockGood\", \"kotlinx-test\"}\n)\npublic final class Kt25937Kt {\n   public static final int callSuspendBlock(@NotNull Function1<? super Continuation<? super Unit>, ? extends Object> block) {\n      Intrinsics.checkParameterIsNotNull(block, \"block\");\n      return 1;\n   }\n\n   public static final int callSuspendBlockGood() {\n      return 1;\n   }\n}\n"
  },
  {
    "path": "testData/kt25937/kt/Kt25937_1Kt.java",
    "content": "package kt;\n\nimport kotlin.Metadata;\nimport kotlin.ResultKt;\nimport kotlin.Unit;\nimport kotlin.coroutines.Continuation;\nimport kotlin.coroutines.intrinsics.IntrinsicsKt;\nimport kotlin.jvm.functions.Function1;\nimport kotlin.jvm.internal.Intrinsics;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\n@Metadata(\n   mv = {1, 1, 17},\n   bv = {1, 0, 3},\n   k = 2,\n   d1 = {\"\\u0000\\b\\n\\u0000\\n\\u0002\\u0010\\b\\n\\u0000\\u001a\\u0006\\u0010\\u0000\\u001a\\u00020\\u0001¨\\u0006\\u0002\"},\n   d2 = {\"some1\", \"\", \"ide-kotlin-test.kotlinx-test.main\"}\n)\npublic final class Kt25937_1Kt {\n   public static final int some1() {\n      return Kt25937Kt.callSuspendBlock((Function1)(new Function1<Continuation<? super Unit>, Object>((Continuation)null) {\n         int label;\n\n         @Nullable\n         public final Object invokeSuspend(@NotNull Object $result) {\n            Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();\n            switch (this.label) {\n               case 0:\n                  ResultKt.throwOnFailure($result);\n                  return Unit.INSTANCE;\n               default:\n                  throw new IllegalStateException(\"call to 'resume' before 'invoke' with coroutine\");\n            }\n         }\n\n         @NotNull\n         public final Continuation<Unit> create(@NotNull Continuation<?> completion) {\n            Intrinsics.checkParameterIsNotNull(completion, \"completion\");\n            Function1 var2 = new <anonymous constructor>(completion);\n            return var2;\n         }\n\n         public final Object invoke(Object var1) {\n            return ((<undefinedtype>)this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);\n         }\n      }));\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a.java",
    "content": "import java.util.Date;\nimport java.util.Iterator;\nimport java.util.List;\n\npublic interface a<K, V> {\n   String a();\n\n   int b();\n\n   int c();\n\n   long d();\n\n   List<Long> e();\n\n   Long f();\n\n   List<Long> g();\n\n   Date h();\n\n   void i();\n\n   void j();\n\n   V a(K var1);\n\n   V a(K var1, f<K, V> var2);\n\n   void a(K var1, V var2);\n\n   void b(K var1);\n\n   boolean c(K var1);\n\n   Iterator<K> k();\n\n   List<b<K, V>> l();\n}\n"
  },
  {
    "path": "testData/obfuscated/a0.java",
    "content": "import java.util.List;\nimport javax.xml.xpath.XPathExpressionException;\n\npublic interface a0 {\n   a0 a(String var1) throws XPathExpressionException;\n\n   List<a0> b(String var1) throws XPathExpressionException;\n\n   a0[] c(String var1) throws XPathExpressionException;\n\n   String d(String var1) throws XPathExpressionException;\n\n   o e(String var1) throws XPathExpressionException;\n\n   boolean f(String var1) throws XPathExpressionException;\n\n   String a();\n}\n"
  },
  {
    "path": "testData/obfuscated/a1.java",
    "content": "import java.util.ArrayList;\nimport java.util.List;\nimport javax.xml.xpath.XPathConstants;\nimport javax.xml.xpath.XPathExpressionException;\nimport javax.xml.xpath.XPathFactory;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\npublic class a1 implements a0 {\n   private Node a;\n   private static final XPathFactory b = XPathFactory.newInstance();\n\n   public a1(Node var1) {\n      this.a = var1;\n   }\n\n   public a0 a(String var1) throws XPathExpressionException {\n      Node var2 = (Node)b.newXPath().compile(var1).evaluate(this.a, XPathConstants.NODE);\n\n      try {\n         if (var2 == null) {\n            return null;\n         }\n      } catch (XPathExpressionException var3) {\n         throw var3;\n      }\n\n      return new a1(var2);\n   }\n\n   public List<a0> b(String var1) throws XPathExpressionException {\n      int var5 = bc.e;\n      NodeList var2 = (NodeList)b.newXPath().compile(var1).evaluate(this.a, XPathConstants.NODESET);\n      ArrayList var3 = new ArrayList(var2.getLength());\n      int var4 = 0;\n\n      ArrayList var10000;\n      while(true) {\n         if (var4 < var2.getLength()) {\n            try {\n               var10000 = var3;\n               if (var5 != 0) {\n                  break;\n               }\n\n               var3.add(new a1(var2.item(var4)));\n               ++var4;\n               if (var5 == 0) {\n                  continue;\n               }\n            } catch (XPathExpressionException var7) {\n               throw var7;\n            }\n\n            int var6 = ap.c;\n            ++var6;\n            ap.c = var6;\n         }\n\n         var10000 = var3;\n         break;\n      }\n\n      return var10000;\n   }\n\n   public a0[] c(String var1) throws XPathExpressionException {\n      List var2 = this.b(var1);\n      return (a0[])var2.toArray(new a0[var2.size()]);\n   }\n\n   public String d(String param1) throws XPathExpressionException {\n      // $FF: Couldn't be decompiled\n   }\n\n   public boolean f(String param1) throws XPathExpressionException {\n      // $FF: Couldn't be decompiled\n   }\n\n   public String a() {\n      return this.a.getNodeName();\n   }\n\n   public String toString() {\n      return this.a();\n   }\n\n   public o e(String var1) throws XPathExpressionException {\n      return o.b((Object)this.d(var1));\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a2.java",
    "content": "import java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.net.URL;\nimport org.xml.sax.EntityResolver;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\n\nclass a2 implements EntityResolver {\n   final bc a;\n   private static final String b;\n\n   a2(bc var1) {\n      this.a = var1;\n   }\n\n   public InputSource resolveEntity(String var1, String var2) throws SAXException, IOException {\n      URL var3 = new URL(var2);\n      if (b.equals(var3.getProtocol())) {\n         File var4 = new File(var3.getFile());\n\n         try {\n            if (var4.exists()) {\n               return new InputSource(new FileInputStream(var4));\n            }\n         } catch (SAXException var5) {\n            throw var5;\n         }\n      }\n\n      return null;\n   }\n\n   static {\n      char[] var10000 = \"\\u001d2\\u00049\".toCharArray();\n      int var10002 = var10000.length;\n      int var1 = 0;\n      char[] var10001 = var10000;\n      int var2 = var10002;\n      int var10003;\n      char[] var4;\n      if (var10002 <= 1) {\n         var4 = var10000;\n         var10003 = var1;\n      } else {\n         var10001 = var10000;\n         var2 = var10002;\n         if (var10002 <= var1) {\n            b = (new String(var10000)).intern();\n            return;\n         }\n\n         var4 = var10000;\n         var10003 = var1;\n      }\n\n      while(true) {\n         char var10004 = var4[var10003];\n         byte var10005;\n         switch (var1 % 5) {\n            case 0:\n               var10005 = 123;\n               break;\n            case 1:\n               var10005 = 91;\n               break;\n            case 2:\n               var10005 = 104;\n               break;\n            case 3:\n               var10005 = 92;\n               break;\n            default:\n               var10005 = 15;\n         }\n\n         var4[var10003] = (char)(var10004 ^ var10005);\n         ++var1;\n         if (var2 == 0) {\n            var10003 = var2;\n            var4 = var10001;\n         } else {\n            if (var2 <= var1) {\n               b = (new String(var10001)).intern();\n               return;\n            }\n\n            var4 = var10001;\n            var10003 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a3.java",
    "content": "import java.util.concurrent.TimeUnit;\n\npublic class a3 {\n   private static final a<Integer, Integer> a;\n   private static final String[] b;\n\n   private static int a(int param0, boolean param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public static void main(String[] var0) throws Exception {\n      boolean var2 = a7.b;\n      ax var1 = ax.a();\n      System.out.println(b[4]);\n      a(42, false);\n      System.out.println(var1.e());\n      System.out.println(b[5]);\n      a(42, true);\n      System.out.println(var1.e());\n      a();\n      System.out.println(b[6]);\n      Thread.sleep(5000L);\n      System.out.println(b[3]);\n      d.b();\n      a();\n      if (var2) {\n         int var3 = ap.c;\n         ++var3;\n         ap.c = var3;\n      }\n\n   }\n\n   private static void a() {\n      System.out.println(b[1] + a.c());\n      System.out.println(b[0] + a.d());\n      System.out.println(b[2] + a.f());\n   }\n\n   static {\n      int var1;\n      int var6;\n      char[] var8;\n      String[] var10000;\n      char[] var10003;\n      char[] var10004;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label697: {\n         var10000 = new String[7];\n         var10003 = \"`eA\\u0006\\u0003\\u0015\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var6 = var10005;\n         if (var10005 <= 1) {\n            var8 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var6 = var10005;\n            if (var10005 <= var1) {\n               break label697;\n            }\n\n            var8 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var8[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 53;\n                  break;\n               case 1:\n                  var10008 = 22;\n                  break;\n               case 2:\n                  var10008 = 36;\n                  break;\n               case 3:\n                  var10008 = 117;\n                  break;\n               default:\n                  var10008 = 57;\n            }\n\n            var8[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var6 == 0) {\n               var10006 = var6;\n               var8 = var10004;\n            } else {\n               if (var6 <= var1) {\n                  break;\n               }\n\n               var8 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"f\\u007f^\\u0010\\u0003\\u0015\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var6 = var10005;\n      char[] var2;\n      int var3;\n      char[] var5;\n      char var9;\n      byte var10;\n      char[] var10001;\n      int var10002;\n      if (var10005 <= 1) {\n         var8 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 53;\n               break;\n            case 1:\n               var10008 = 22;\n               break;\n            case 2:\n               var10008 = 36;\n               break;\n            case 3:\n               var10008 = 117;\n               break;\n            default:\n               var10008 = 57;\n         }\n      } else {\n         var10004 = var10003;\n         var6 = var10005;\n         if (var10005 <= var1) {\n            label740: {\n               var10000[1] = (new String(var10003)).intern();\n               var10003 = \"}\\u007fPXkTbAO\\u0019\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var6 = var10005;\n               if (var10005 <= 1) {\n                  var8 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var6 = var10005;\n                  if (var10005 <= var1) {\n                     break label740;\n                  }\n\n                  var8 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var8[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 53;\n                        break;\n                     case 1:\n                        var10008 = 22;\n                        break;\n                     case 2:\n                        var10008 = 36;\n                        break;\n                     case 3:\n                        var10008 = 117;\n                        break;\n                     default:\n                        var10008 = 57;\n                  }\n\n                  var8[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var6 == 0) {\n                     var10006 = var6;\n                     var8 = var10004;\n                  } else {\n                     if (var6 <= var1) {\n                        break;\n                     }\n\n                     var8 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[2] = (new String(var10004)).intern();\n            var10003 = \"gcJ\\u001bP[q\\u0004\\u0016XV~AX\\\\C\\u007fG\\u0001PZx\\n[\\u0017\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var6 = var10005;\n            if (var10005 <= 1) {\n               var8 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 53;\n                     break;\n                  case 1:\n                     var10008 = 22;\n                     break;\n                  case 2:\n                     var10008 = 36;\n                     break;\n                  case 3:\n                     var10008 = 117;\n                     break;\n                  default:\n                     var10008 = 57;\n               }\n            } else {\n               var10004 = var10003;\n               var6 = var10005;\n               if (var10005 <= var1) {\n                  label808: {\n                     var10000[3] = (new String(var10003)).intern();\n                     var10003 = \"S\\u007fF]\\r\\u0007?\\u0004\\u0002PA~K\\u0000M\\u0015uE\\u0016QP,\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var6 = var10005;\n                     if (var10005 <= 1) {\n                        var8 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= var1) {\n                           break label808;\n                        }\n\n                        var8 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var8[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 53;\n                              break;\n                           case 1:\n                              var10008 = 22;\n                              break;\n                           case 2:\n                              var10008 = 36;\n                              break;\n                           case 3:\n                              var10008 = 117;\n                              break;\n                           default:\n                              var10008 = 57;\n                        }\n\n                        var8[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var6 == 0) {\n                           var10006 = var6;\n                           var8 = var10004;\n                        } else {\n                           if (var6 <= var1) {\n                              break;\n                           }\n\n                           var8 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[4] = (new String(var10004)).intern();\n                  var10003 = \"S\\u007fF]\\r\\u0007?\\u0004\\u0002PA~\\u0004\\u0016XV~AO\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var6 = var10005;\n                  if (var10005 <= 1) {\n                     var8 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 53;\n                           break;\n                        case 1:\n                           var10008 = 22;\n                           break;\n                        case 2:\n                           var10008 = 36;\n                           break;\n                        case 3:\n                           var10008 = 117;\n                           break;\n                        default:\n                           var10008 = 57;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var6 = var10005;\n                     if (var10005 <= var1) {\n                        label876: {\n                           var10000[5] = (new String(var10003)).intern();\n                           var10003 = \"bwM\\u0001P[q\\u0004@\\u0019FsG\\u001aWQe\\u0004\\u0001V\\u0015zA\\u0001\\u0019ZcVU\\\\[bV\\u001c\\\\F6A\\rI\\\\dA[\\u0017\\u001b\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= 1) {\n                              var8 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= var1) {\n                                 break label876;\n                              }\n\n                              var8 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var8[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 53;\n                                    break;\n                                 case 1:\n                                    var10008 = 22;\n                                    break;\n                                 case 2:\n                                    var10008 = 36;\n                                    break;\n                                 case 3:\n                                    var10008 = 117;\n                                    break;\n                                 default:\n                                    var10008 = 57;\n                              }\n\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                              } else {\n                                 if (var6 <= var1) {\n                                    break;\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[6] = (new String(var10004)).intern();\n                        b = var10000;\n                        var2 = \"s\\u007fF\\u001aWTuG\\u001c\".toCharArray();\n                        var10002 = var2.length;\n                        var1 = 0;\n                        var10001 = var2;\n                        var3 = var10002;\n                        if (var10002 <= 1) {\n                           var5 = var2;\n                           var6 = var1;\n                        } else {\n                           var10001 = var2;\n                           var3 = var10002;\n                           if (var10002 <= var1) {\n                              a = d.a((new String(var2)).intern(), 10, 2L, TimeUnit.SECONDS);\n                              return;\n                           }\n\n                           var5 = var2;\n                           var6 = var1;\n                        }\n\n                        while(true) {\n                           var9 = var5[var6];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10 = 53;\n                                 break;\n                              case 1:\n                                 var10 = 22;\n                                 break;\n                              case 2:\n                                 var10 = 36;\n                                 break;\n                              case 3:\n                                 var10 = 117;\n                                 break;\n                              default:\n                                 var10 = 57;\n                           }\n\n                           var5[var6] = (char)(var9 ^ var10);\n                           ++var1;\n                           if (var3 == 0) {\n                              var6 = var3;\n                              var5 = var10001;\n                           } else {\n                              if (var3 <= var1) {\n                                 a = d.a((new String(var10001)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                 return;\n                              }\n\n                              var5 = var10001;\n                              var6 = var1;\n                           }\n                        }\n                     }\n\n                     var8 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 53;\n                           break;\n                        case 1:\n                           var10008 = 22;\n                           break;\n                        case 2:\n                           var10008 = 36;\n                           break;\n                        case 3:\n                           var10008 = 117;\n                           break;\n                        default:\n                           var10008 = 57;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var8[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var6 == 0) {\n                           var10006 = var6;\n                           var8 = var10004;\n                           var10007 = var10004[var6];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 53;\n                                 break;\n                              case 1:\n                                 var10008 = 22;\n                                 break;\n                              case 2:\n                                 var10008 = 36;\n                                 break;\n                              case 3:\n                                 var10008 = 117;\n                                 break;\n                              default:\n                                 var10008 = 57;\n                           }\n                        } else {\n                           if (var6 <= var1) {\n                              label984: {\n                                 var10000[5] = (new String(var10004)).intern();\n                                 var10003 = \"bwM\\u0001P[q\\u0004@\\u0019FsG\\u001aWQe\\u0004\\u0001V\\u0015zA\\u0001\\u0019ZcVU\\\\[bV\\u001c\\\\F6A\\rI\\\\dA[\\u0017\\u001b\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label984;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 53;\n                                          break;\n                                       case 1:\n                                          var10008 = 22;\n                                          break;\n                                       case 2:\n                                          var10008 = 36;\n                                          break;\n                                       case 3:\n                                          var10008 = 117;\n                                          break;\n                                       default:\n                                          var10008 = 57;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[6] = (new String(var10004)).intern();\n                              b = var10000;\n                              var2 = \"s\\u007fF\\u001aWTuG\\u001c\".toCharArray();\n                              var10002 = var2.length;\n                              var1 = 0;\n                              var10001 = var2;\n                              var3 = var10002;\n                              if (var10002 <= 1) {\n                                 var5 = var2;\n                                 var6 = var1;\n                              } else {\n                                 var10001 = var2;\n                                 var3 = var10002;\n                                 if (var10002 <= var1) {\n                                    a = d.a((new String(var2)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                    return;\n                                 }\n\n                                 var5 = var2;\n                                 var6 = var1;\n                              }\n\n                              while(true) {\n                                 var9 = var5[var6];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10 = 53;\n                                       break;\n                                    case 1:\n                                       var10 = 22;\n                                       break;\n                                    case 2:\n                                       var10 = 36;\n                                       break;\n                                    case 3:\n                                       var10 = 117;\n                                       break;\n                                    default:\n                                       var10 = 57;\n                                 }\n\n                                 var5[var6] = (char)(var9 ^ var10);\n                                 ++var1;\n                                 if (var3 == 0) {\n                                    var6 = var3;\n                                    var5 = var10001;\n                                 } else {\n                                    if (var3 <= var1) {\n                                       a = d.a((new String(var10001)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                       return;\n                                    }\n\n                                    var5 = var10001;\n                                    var6 = var1;\n                                 }\n                              }\n                           }\n\n                           var8 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 53;\n                                 break;\n                              case 1:\n                                 var10008 = 22;\n                                 break;\n                              case 2:\n                                 var10008 = 36;\n                                 break;\n                              case 3:\n                                 var10008 = 117;\n                                 break;\n                              default:\n                                 var10008 = 57;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var8 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 53;\n                     break;\n                  case 1:\n                     var10008 = 22;\n                     break;\n                  case 2:\n                     var10008 = 36;\n                     break;\n                  case 3:\n                     var10008 = 117;\n                     break;\n                  default:\n                     var10008 = 57;\n               }\n            }\n\n            while(true) {\n               while(true) {\n                  var8[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var6 == 0) {\n                     var10006 = var6;\n                     var8 = var10004;\n                     var10007 = var10004[var6];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 53;\n                           break;\n                        case 1:\n                           var10008 = 22;\n                           break;\n                        case 2:\n                           var10008 = 36;\n                           break;\n                        case 3:\n                           var10008 = 117;\n                           break;\n                        default:\n                           var10008 = 57;\n                     }\n                  } else {\n                     if (var6 <= var1) {\n                        label1119: {\n                           var10000[3] = (new String(var10004)).intern();\n                           var10003 = \"S\\u007fF]\\r\\u0007?\\u0004\\u0002PA~K\\u0000M\\u0015uE\\u0016QP,\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= 1) {\n                              var8 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= var1) {\n                                 break label1119;\n                              }\n\n                              var8 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var8[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 53;\n                                    break;\n                                 case 1:\n                                    var10008 = 22;\n                                    break;\n                                 case 2:\n                                    var10008 = 36;\n                                    break;\n                                 case 3:\n                                    var10008 = 117;\n                                    break;\n                                 default:\n                                    var10008 = 57;\n                              }\n\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                              } else {\n                                 if (var6 <= var1) {\n                                    break;\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[4] = (new String(var10004)).intern();\n                        var10003 = \"S\\u007fF]\\r\\u0007?\\u0004\\u0002PA~\\u0004\\u0016XV~AO\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= 1) {\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 53;\n                                 break;\n                              case 1:\n                                 var10008 = 22;\n                                 break;\n                              case 2:\n                                 var10008 = 36;\n                                 break;\n                              case 3:\n                                 var10008 = 117;\n                                 break;\n                              default:\n                                 var10008 = 57;\n                           }\n                        } else {\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= var1) {\n                              label1187: {\n                                 var10000[5] = (new String(var10003)).intern();\n                                 var10003 = \"bwM\\u0001P[q\\u0004@\\u0019FsG\\u001aWQe\\u0004\\u0001V\\u0015zA\\u0001\\u0019ZcVU\\\\[bV\\u001c\\\\F6A\\rI\\\\dA[\\u0017\\u001b\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label1187;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 53;\n                                          break;\n                                       case 1:\n                                          var10008 = 22;\n                                          break;\n                                       case 2:\n                                          var10008 = 36;\n                                          break;\n                                       case 3:\n                                          var10008 = 117;\n                                          break;\n                                       default:\n                                          var10008 = 57;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[6] = (new String(var10004)).intern();\n                              b = var10000;\n                              var2 = \"s\\u007fF\\u001aWTuG\\u001c\".toCharArray();\n                              var10002 = var2.length;\n                              var1 = 0;\n                              var10001 = var2;\n                              var3 = var10002;\n                              if (var10002 <= 1) {\n                                 var5 = var2;\n                                 var6 = var1;\n                              } else {\n                                 var10001 = var2;\n                                 var3 = var10002;\n                                 if (var10002 <= var1) {\n                                    a = d.a((new String(var2)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                    return;\n                                 }\n\n                                 var5 = var2;\n                                 var6 = var1;\n                              }\n\n                              while(true) {\n                                 var9 = var5[var6];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10 = 53;\n                                       break;\n                                    case 1:\n                                       var10 = 22;\n                                       break;\n                                    case 2:\n                                       var10 = 36;\n                                       break;\n                                    case 3:\n                                       var10 = 117;\n                                       break;\n                                    default:\n                                       var10 = 57;\n                                 }\n\n                                 var5[var6] = (char)(var9 ^ var10);\n                                 ++var1;\n                                 if (var3 == 0) {\n                                    var6 = var3;\n                                    var5 = var10001;\n                                 } else {\n                                    if (var3 <= var1) {\n                                       a = d.a((new String(var10001)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                       return;\n                                    }\n\n                                    var5 = var10001;\n                                    var6 = var1;\n                                 }\n                              }\n                           }\n\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 53;\n                                 break;\n                              case 1:\n                                 var10008 = 22;\n                                 break;\n                              case 2:\n                                 var10008 = 36;\n                                 break;\n                              case 3:\n                                 var10008 = 117;\n                                 break;\n                              default:\n                                 var10008 = 57;\n                           }\n                        }\n\n                        while(true) {\n                           while(true) {\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                                 var10007 = var10004[var6];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 53;\n                                       break;\n                                    case 1:\n                                       var10008 = 22;\n                                       break;\n                                    case 2:\n                                       var10008 = 36;\n                                       break;\n                                    case 3:\n                                       var10008 = 117;\n                                       break;\n                                    default:\n                                       var10008 = 57;\n                                 }\n                              } else {\n                                 if (var6 <= var1) {\n                                    label1295: {\n                                       var10000[5] = (new String(var10004)).intern();\n                                       var10003 = \"bwM\\u0001P[q\\u0004@\\u0019FsG\\u001aWQe\\u0004\\u0001V\\u0015zA\\u0001\\u0019ZcVU\\\\[bV\\u001c\\\\F6A\\rI\\\\dA[\\u0017\\u001b\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label1295;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 53;\n                                                break;\n                                             case 1:\n                                                var10008 = 22;\n                                                break;\n                                             case 2:\n                                                var10008 = 36;\n                                                break;\n                                             case 3:\n                                                var10008 = 117;\n                                                break;\n                                             default:\n                                                var10008 = 57;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[6] = (new String(var10004)).intern();\n                                    b = var10000;\n                                    var2 = \"s\\u007fF\\u001aWTuG\\u001c\".toCharArray();\n                                    var10002 = var2.length;\n                                    var1 = 0;\n                                    var10001 = var2;\n                                    var3 = var10002;\n                                    if (var10002 <= 1) {\n                                       var5 = var2;\n                                       var6 = var1;\n                                    } else {\n                                       var10001 = var2;\n                                       var3 = var10002;\n                                       if (var10002 <= var1) {\n                                          a = d.a((new String(var2)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                          return;\n                                       }\n\n                                       var5 = var2;\n                                       var6 = var1;\n                                    }\n\n                                    while(true) {\n                                       var9 = var5[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10 = 53;\n                                             break;\n                                          case 1:\n                                             var10 = 22;\n                                             break;\n                                          case 2:\n                                             var10 = 36;\n                                             break;\n                                          case 3:\n                                             var10 = 117;\n                                             break;\n                                          default:\n                                             var10 = 57;\n                                       }\n\n                                       var5[var6] = (char)(var9 ^ var10);\n                                       ++var1;\n                                       if (var3 == 0) {\n                                          var6 = var3;\n                                          var5 = var10001;\n                                       } else {\n                                          if (var3 <= var1) {\n                                             a = d.a((new String(var10001)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                             return;\n                                          }\n\n                                          var5 = var10001;\n                                          var6 = var1;\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                                 var10007 = var10004[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 53;\n                                       break;\n                                    case 1:\n                                       var10008 = 22;\n                                       break;\n                                    case 2:\n                                       var10008 = 36;\n                                       break;\n                                    case 3:\n                                       var10008 = 117;\n                                       break;\n                                    default:\n                                       var10008 = 57;\n                                 }\n                              }\n                           }\n                        }\n                     }\n\n                     var8 = var10004;\n                     var10006 = var1;\n                     var10007 = var10004[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 53;\n                           break;\n                        case 1:\n                           var10008 = 22;\n                           break;\n                        case 2:\n                           var10008 = 36;\n                           break;\n                        case 3:\n                           var10008 = 117;\n                           break;\n                        default:\n                           var10008 = 57;\n                     }\n                  }\n               }\n            }\n         }\n\n         var8 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 53;\n               break;\n            case 1:\n               var10008 = 22;\n               break;\n            case 2:\n               var10008 = 36;\n               break;\n            case 3:\n               var10008 = 117;\n               break;\n            default:\n               var10008 = 57;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var8[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var6 == 0) {\n               var10006 = var6;\n               var8 = var10004;\n               var10007 = var10004[var6];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 53;\n                     break;\n                  case 1:\n                     var10008 = 22;\n                     break;\n                  case 2:\n                     var10008 = 36;\n                     break;\n                  case 3:\n                     var10008 = 117;\n                     break;\n                  default:\n                     var10008 = 57;\n               }\n            } else {\n               if (var6 <= var1) {\n                  label333: {\n                     var10000[1] = (new String(var10004)).intern();\n                     var10003 = \"}\\u007fPXkTbAO\\u0019\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var6 = var10005;\n                     if (var10005 <= 1) {\n                        var8 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= var1) {\n                           break label333;\n                        }\n\n                        var8 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var8[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 53;\n                              break;\n                           case 1:\n                              var10008 = 22;\n                              break;\n                           case 2:\n                              var10008 = 36;\n                              break;\n                           case 3:\n                              var10008 = 117;\n                              break;\n                           default:\n                              var10008 = 57;\n                        }\n\n                        var8[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var6 == 0) {\n                           var10006 = var6;\n                           var8 = var10004;\n                        } else {\n                           if (var6 <= var1) {\n                              break;\n                           }\n\n                           var8 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[2] = (new String(var10004)).intern();\n                  var10003 = \"gcJ\\u001bP[q\\u0004\\u0016XV~AX\\\\C\\u007fG\\u0001PZx\\n[\\u0017\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var6 = var10005;\n                  if (var10005 <= 1) {\n                     var8 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 53;\n                           break;\n                        case 1:\n                           var10008 = 22;\n                           break;\n                        case 2:\n                           var10008 = 36;\n                           break;\n                        case 3:\n                           var10008 = 117;\n                           break;\n                        default:\n                           var10008 = 57;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var6 = var10005;\n                     if (var10005 <= var1) {\n                        label377: {\n                           var10000[3] = (new String(var10003)).intern();\n                           var10003 = \"S\\u007fF]\\r\\u0007?\\u0004\\u0002PA~K\\u0000M\\u0015uE\\u0016QP,\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= 1) {\n                              var8 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= var1) {\n                                 break label377;\n                              }\n\n                              var8 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var8[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 53;\n                                    break;\n                                 case 1:\n                                    var10008 = 22;\n                                    break;\n                                 case 2:\n                                    var10008 = 36;\n                                    break;\n                                 case 3:\n                                    var10008 = 117;\n                                    break;\n                                 default:\n                                    var10008 = 57;\n                              }\n\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                              } else {\n                                 if (var6 <= var1) {\n                                    break;\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[4] = (new String(var10004)).intern();\n                        var10003 = \"S\\u007fF]\\r\\u0007?\\u0004\\u0002PA~\\u0004\\u0016XV~AO\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= 1) {\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 53;\n                                 break;\n                              case 1:\n                                 var10008 = 22;\n                                 break;\n                              case 2:\n                                 var10008 = 36;\n                                 break;\n                              case 3:\n                                 var10008 = 117;\n                                 break;\n                              default:\n                                 var10008 = 57;\n                           }\n                        } else {\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= var1) {\n                              label445: {\n                                 var10000[5] = (new String(var10003)).intern();\n                                 var10003 = \"bwM\\u0001P[q\\u0004@\\u0019FsG\\u001aWQe\\u0004\\u0001V\\u0015zA\\u0001\\u0019ZcVU\\\\[bV\\u001c\\\\F6A\\rI\\\\dA[\\u0017\\u001b\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label445;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 53;\n                                          break;\n                                       case 1:\n                                          var10008 = 22;\n                                          break;\n                                       case 2:\n                                          var10008 = 36;\n                                          break;\n                                       case 3:\n                                          var10008 = 117;\n                                          break;\n                                       default:\n                                          var10008 = 57;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[6] = (new String(var10004)).intern();\n                              b = var10000;\n                              var2 = \"s\\u007fF\\u001aWTuG\\u001c\".toCharArray();\n                              var10002 = var2.length;\n                              var1 = 0;\n                              var10001 = var2;\n                              var3 = var10002;\n                              if (var10002 <= 1) {\n                                 var5 = var2;\n                                 var6 = var1;\n                              } else {\n                                 var10001 = var2;\n                                 var3 = var10002;\n                                 if (var10002 <= var1) {\n                                    a = d.a((new String(var2)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                    return;\n                                 }\n\n                                 var5 = var2;\n                                 var6 = var1;\n                              }\n\n                              while(true) {\n                                 var9 = var5[var6];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10 = 53;\n                                       break;\n                                    case 1:\n                                       var10 = 22;\n                                       break;\n                                    case 2:\n                                       var10 = 36;\n                                       break;\n                                    case 3:\n                                       var10 = 117;\n                                       break;\n                                    default:\n                                       var10 = 57;\n                                 }\n\n                                 var5[var6] = (char)(var9 ^ var10);\n                                 ++var1;\n                                 if (var3 == 0) {\n                                    var6 = var3;\n                                    var5 = var10001;\n                                 } else {\n                                    if (var3 <= var1) {\n                                       a = d.a((new String(var10001)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                       return;\n                                    }\n\n                                    var5 = var10001;\n                                    var6 = var1;\n                                 }\n                              }\n                           }\n\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 53;\n                                 break;\n                              case 1:\n                                 var10008 = 22;\n                                 break;\n                              case 2:\n                                 var10008 = 36;\n                                 break;\n                              case 3:\n                                 var10008 = 117;\n                                 break;\n                              default:\n                                 var10008 = 57;\n                           }\n                        }\n\n                        while(true) {\n                           while(true) {\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                                 var10007 = var10004[var6];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 53;\n                                       break;\n                                    case 1:\n                                       var10008 = 22;\n                                       break;\n                                    case 2:\n                                       var10008 = 36;\n                                       break;\n                                    case 3:\n                                       var10008 = 117;\n                                       break;\n                                    default:\n                                       var10008 = 57;\n                                 }\n                              } else {\n                                 if (var6 <= var1) {\n                                    label553: {\n                                       var10000[5] = (new String(var10004)).intern();\n                                       var10003 = \"bwM\\u0001P[q\\u0004@\\u0019FsG\\u001aWQe\\u0004\\u0001V\\u0015zA\\u0001\\u0019ZcVU\\\\[bV\\u001c\\\\F6A\\rI\\\\dA[\\u0017\\u001b\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label553;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 53;\n                                                break;\n                                             case 1:\n                                                var10008 = 22;\n                                                break;\n                                             case 2:\n                                                var10008 = 36;\n                                                break;\n                                             case 3:\n                                                var10008 = 117;\n                                                break;\n                                             default:\n                                                var10008 = 57;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[6] = (new String(var10004)).intern();\n                                    b = var10000;\n                                    var2 = \"s\\u007fF\\u001aWTuG\\u001c\".toCharArray();\n                                    var10002 = var2.length;\n                                    var1 = 0;\n                                    var10001 = var2;\n                                    var3 = var10002;\n                                    if (var10002 <= 1) {\n                                       var5 = var2;\n                                       var6 = var1;\n                                    } else {\n                                       var10001 = var2;\n                                       var3 = var10002;\n                                       if (var10002 <= var1) {\n                                          a = d.a((new String(var2)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                          return;\n                                       }\n\n                                       var5 = var2;\n                                       var6 = var1;\n                                    }\n\n                                    while(true) {\n                                       var9 = var5[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10 = 53;\n                                             break;\n                                          case 1:\n                                             var10 = 22;\n                                             break;\n                                          case 2:\n                                             var10 = 36;\n                                             break;\n                                          case 3:\n                                             var10 = 117;\n                                             break;\n                                          default:\n                                             var10 = 57;\n                                       }\n\n                                       var5[var6] = (char)(var9 ^ var10);\n                                       ++var1;\n                                       if (var3 == 0) {\n                                          var6 = var3;\n                                          var5 = var10001;\n                                       } else {\n                                          if (var3 <= var1) {\n                                             a = d.a((new String(var10001)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                             return;\n                                          }\n\n                                          var5 = var10001;\n                                          var6 = var1;\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                                 var10007 = var10004[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 53;\n                                       break;\n                                    case 1:\n                                       var10008 = 22;\n                                       break;\n                                    case 2:\n                                       var10008 = 36;\n                                       break;\n                                    case 3:\n                                       var10008 = 117;\n                                       break;\n                                    default:\n                                       var10008 = 57;\n                                 }\n                              }\n                           }\n                        }\n                     }\n\n                     var8 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 53;\n                           break;\n                        case 1:\n                           var10008 = 22;\n                           break;\n                        case 2:\n                           var10008 = 36;\n                           break;\n                        case 3:\n                           var10008 = 117;\n                           break;\n                        default:\n                           var10008 = 57;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var8[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var6 == 0) {\n                           var10006 = var6;\n                           var8 = var10004;\n                           var10007 = var10004[var6];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 53;\n                                 break;\n                              case 1:\n                                 var10008 = 22;\n                                 break;\n                              case 2:\n                                 var10008 = 36;\n                                 break;\n                              case 3:\n                                 var10008 = 117;\n                                 break;\n                              default:\n                                 var10008 = 57;\n                           }\n                        } else {\n                           if (var6 <= var1) {\n                              label172: {\n                                 var10000[3] = (new String(var10004)).intern();\n                                 var10003 = \"S\\u007fF]\\r\\u0007?\\u0004\\u0002PA~K\\u0000M\\u0015uE\\u0016QP,\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label172;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 53;\n                                          break;\n                                       case 1:\n                                          var10008 = 22;\n                                          break;\n                                       case 2:\n                                          var10008 = 36;\n                                          break;\n                                       case 3:\n                                          var10008 = 117;\n                                          break;\n                                       default:\n                                          var10008 = 57;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[4] = (new String(var10004)).intern();\n                              var10003 = \"S\\u007fF]\\r\\u0007?\\u0004\\u0002PA~\\u0004\\u0016XV~AO\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= 1) {\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 53;\n                                       break;\n                                    case 1:\n                                       var10008 = 22;\n                                       break;\n                                    case 2:\n                                       var10008 = 36;\n                                       break;\n                                    case 3:\n                                       var10008 = 117;\n                                       break;\n                                    default:\n                                       var10008 = 57;\n                                 }\n                              } else {\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= var1) {\n                                    label216: {\n                                       var10000[5] = (new String(var10003)).intern();\n                                       var10003 = \"bwM\\u0001P[q\\u0004@\\u0019FsG\\u001aWQe\\u0004\\u0001V\\u0015zA\\u0001\\u0019ZcVU\\\\[bV\\u001c\\\\F6A\\rI\\\\dA[\\u0017\\u001b\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label216;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 53;\n                                                break;\n                                             case 1:\n                                                var10008 = 22;\n                                                break;\n                                             case 2:\n                                                var10008 = 36;\n                                                break;\n                                             case 3:\n                                                var10008 = 117;\n                                                break;\n                                             default:\n                                                var10008 = 57;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[6] = (new String(var10004)).intern();\n                                    b = var10000;\n                                    var2 = \"s\\u007fF\\u001aWTuG\\u001c\".toCharArray();\n                                    var10002 = var2.length;\n                                    var1 = 0;\n                                    var10001 = var2;\n                                    var3 = var10002;\n                                    if (var10002 <= 1) {\n                                       var5 = var2;\n                                       var6 = var1;\n                                    } else {\n                                       var10001 = var2;\n                                       var3 = var10002;\n                                       if (var10002 <= var1) {\n                                          a = d.a((new String(var2)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                          return;\n                                       }\n\n                                       var5 = var2;\n                                       var6 = var1;\n                                    }\n\n                                    while(true) {\n                                       var9 = var5[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10 = 53;\n                                             break;\n                                          case 1:\n                                             var10 = 22;\n                                             break;\n                                          case 2:\n                                             var10 = 36;\n                                             break;\n                                          case 3:\n                                             var10 = 117;\n                                             break;\n                                          default:\n                                             var10 = 57;\n                                       }\n\n                                       var5[var6] = (char)(var9 ^ var10);\n                                       ++var1;\n                                       if (var3 == 0) {\n                                          var6 = var3;\n                                          var5 = var10001;\n                                       } else {\n                                          if (var3 <= var1) {\n                                             a = d.a((new String(var10001)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                             return;\n                                          }\n\n                                          var5 = var10001;\n                                          var6 = var1;\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 53;\n                                       break;\n                                    case 1:\n                                       var10008 = 22;\n                                       break;\n                                    case 2:\n                                       var10008 = 36;\n                                       break;\n                                    case 3:\n                                       var10008 = 117;\n                                       break;\n                                    default:\n                                       var10008 = 57;\n                                 }\n                              }\n\n                              while(true) {\n                                 while(true) {\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                       var10007 = var10004[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 53;\n                                             break;\n                                          case 1:\n                                             var10008 = 22;\n                                             break;\n                                          case 2:\n                                             var10008 = 36;\n                                             break;\n                                          case 3:\n                                             var10008 = 117;\n                                             break;\n                                          default:\n                                             var10008 = 57;\n                                       }\n                                    } else {\n                                       if (var6 <= var1) {\n                                          label136: {\n                                             var10000[5] = (new String(var10004)).intern();\n                                             var10003 = \"bwM\\u0001P[q\\u0004@\\u0019FsG\\u001aWQe\\u0004\\u0001V\\u0015zA\\u0001\\u0019ZcVU\\\\[bV\\u001c\\\\F6A\\rI\\\\dA[\\u0017\\u001b\".toCharArray();\n                                             var10005 = var10003.length;\n                                             var1 = 0;\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= 1) {\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             } else {\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= var1) {\n                                                   break label136;\n                                                }\n\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             }\n\n                                             while(true) {\n                                                var10007 = var8[var10006];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10008 = 53;\n                                                      break;\n                                                   case 1:\n                                                      var10008 = 22;\n                                                      break;\n                                                   case 2:\n                                                      var10008 = 36;\n                                                      break;\n                                                   case 3:\n                                                      var10008 = 117;\n                                                      break;\n                                                   default:\n                                                      var10008 = 57;\n                                                }\n\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          var10000[6] = (new String(var10004)).intern();\n                                          b = var10000;\n                                          var2 = \"s\\u007fF\\u001aWTuG\\u001c\".toCharArray();\n                                          var10002 = var2.length;\n                                          var1 = 0;\n                                          var10001 = var2;\n                                          var3 = var10002;\n                                          if (var10002 <= 1) {\n                                             var5 = var2;\n                                             var6 = var1;\n                                          } else {\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= var1) {\n                                                a = d.a((new String(var2)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                                return;\n                                             }\n\n                                             var5 = var2;\n                                             var6 = var1;\n                                          }\n\n                                          while(true) {\n                                             var9 = var5[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10 = 53;\n                                                   break;\n                                                case 1:\n                                                   var10 = 22;\n                                                   break;\n                                                case 2:\n                                                   var10 = 36;\n                                                   break;\n                                                case 3:\n                                                   var10 = 117;\n                                                   break;\n                                                default:\n                                                   var10 = 57;\n                                             }\n\n                                             var5[var6] = (char)(var9 ^ var10);\n                                             ++var1;\n                                             if (var3 == 0) {\n                                                var6 = var3;\n                                                var5 = var10001;\n                                             } else {\n                                                if (var3 <= var1) {\n                                                   a = d.a((new String(var10001)).intern(), 10, 2L, TimeUnit.SECONDS);\n                                                   return;\n                                                }\n\n                                                var5 = var10001;\n                                                var6 = var1;\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                       var10007 = var10004[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 53;\n                                             break;\n                                          case 1:\n                                             var10008 = 22;\n                                             break;\n                                          case 2:\n                                             var10008 = 36;\n                                             break;\n                                          case 3:\n                                             var10008 = 117;\n                                             break;\n                                          default:\n                                             var10008 = 57;\n                                       }\n                                    }\n                                 }\n                              }\n                           }\n\n                           var8 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 53;\n                                 break;\n                              case 1:\n                                 var10008 = 22;\n                                 break;\n                              case 2:\n                                 var10008 = 36;\n                                 break;\n                              case 3:\n                                 var10008 = 117;\n                                 break;\n                              default:\n                                 var10008 = 57;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var8 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 53;\n                     break;\n                  case 1:\n                     var10008 = 22;\n                     break;\n                  case 2:\n                     var10008 = 36;\n                     break;\n                  case 3:\n                     var10008 = 117;\n                     break;\n                  default:\n                     var10008 = 57;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a4.java",
    "content": "import java.text.DateFormat;\nimport java.util.Date;\nimport java.util.logging.Level;\n\n@aa(\n   a = {ac.class}\n)\npublic class a4 implements ac {\n   private static boolean a;\n   private static y<ad> b;\n   private static final String[] c;\n\n   public static void main(String[] var0) throws Exception {\n      t.a.setLevel(Level.FINE);\n      t.a();\n      a = true;\n\n      while(true) {\n         Thread.sleep(10000L);\n         System.out.println(c[1] + ((ad)b.a()).a());\n      }\n   }\n\n   public void a() throws Exception {\n      try {\n         if (a) {\n            System.out.println(c[0] + DateFormat.getTimeInstance().format(new Date()));\n         }\n\n      } catch (Exception var1) {\n         throw var1;\n      }\n   }\n\n   static {\n      String[] var10000;\n      int var1;\n      int var2;\n      char[] var10003;\n      char[] var10004;\n      char[] var4;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label51: {\n         var10000 = new String[2];\n         var10003 = \"f\\u0019\\u0016n-[\\u001c\\u0016n0AKS\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= 1) {\n            var4 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= var1) {\n               break label51;\n            }\n\n            var4 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var4[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 50;\n                  break;\n               case 1:\n                  var10008 = 113;\n                  break;\n               case 2:\n                  var10008 = 115;\n                  break;\n               case 3:\n                  var10008 = 78;\n                  break;\n               default:\n                  var10008 = 89;\n            }\n\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"~\\u0010\\u0000:y[\\u001f\\u0005!:S\\u0005\\u001a!7\\bQ\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var2 = var10005;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            var10000[1] = (new String(var10003)).intern();\n            c = var10000;\n            a = false;\n            b = y.a(ad.class);\n            return;\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n      }\n\n      while(true) {\n         var10007 = var4[var10006];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 50;\n               break;\n            case 1:\n               var10008 = 113;\n               break;\n            case 2:\n               var10008 = 115;\n               break;\n            case 3:\n               var10008 = 78;\n               break;\n            default:\n               var10008 = 89;\n         }\n\n         var4[var10006] = (char)(var10007 ^ var10008);\n         ++var1;\n         if (var2 == 0) {\n            var10006 = var2;\n            var4 = var10004;\n         } else {\n            if (var2 <= var1) {\n               var10000[1] = (new String(var10004)).intern();\n               c = var10000;\n               a = false;\n               b = y.a(ad.class);\n               return;\n            }\n\n            var4 = var10004;\n            var10006 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a5.java",
    "content": "import java.text.DecimalFormat;\nimport java.util.Iterator;\nimport java.util.concurrent.TimeUnit;\n\npublic class a5 {\n   private static y<ai> a;\n   private static final String[] b;\n\n   public static void main(String[] var0) throws Exception {\n      t.a();\n\n      while(true) {\n         System.out.println(b[1]);\n         Thread.sleep(TimeUnit.MILLISECONDS.convert(60L, TimeUnit.SECONDS));\n         a();\n         System.out.println(b[0]);\n      }\n   }\n\n   private static void a() {\n      boolean var2 = a7.b;\n      Iterator var0 = ((ai)a.a()).a().iterator();\n\n      while(var0.hasNext()) {\n         al var1 = (al)var0.next();\n         System.out.println(var1.a().a() + b[3] + var1.a().b() + \"\\n\" + DecimalFormat.getNumberInstance().format(var1.d()) + \" \" + var1.a().c() + b[4] + DecimalFormat.getNumberInstance().format(var1.e()) + \" \" + var1.a().c() + b[2] + DecimalFormat.getNumberInstance().format(var1.f()) + \" \" + var1.a().c() + \"\\n\");\n         if (var2) {\n            break;\n         }\n      }\n\n   }\n\n   static {\n      String[] var10000 = new String[5];\n      char[] var10003 = \"`\\u001dY>>`\\u001dY>>`\\u001dY>>`\\u001dY>>`\\u001dY>>`\\u001dY>>`\\u001dY>\".toCharArray();\n      int var10005 = var10003.length;\n      int var1 = 0;\n      char[] var10004 = var10003;\n      int var2 = var10005;\n      char[] var4;\n      int var10006;\n      char var10007;\n      byte var10008;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 77;\n               break;\n            case 1:\n               var10008 = 48;\n               break;\n            case 2:\n               var10008 = 116;\n               break;\n            case 3:\n               var10008 = 19;\n               break;\n            default:\n               var10008 = 19;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label316: {\n               var10000[0] = (new String(var10003)).intern();\n               var10003 = \"\\u001aQ\\u001dgz#WT|}(\\u0010\\u0019z}8D\\u0011==c\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label316;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 77;\n                        break;\n                     case 1:\n                        var10008 = 48;\n                        break;\n                     case 2:\n                        var10008 = 116;\n                        break;\n                     case 3:\n                        var10008 = 19;\n                        break;\n                     default:\n                        var10008 = 19;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[1] = (new String(var10004)).intern();\n            var10003 = \"a\\u00105etm\\u0002@{)m\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 77;\n                     break;\n                  case 1:\n                     var10008 = 48;\n                     break;\n                  case 2:\n                     var10008 = 116;\n                     break;\n                  case 3:\n                     var10008 = 19;\n                     break;\n                  default:\n                     var10008 = 19;\n               }\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  label384: {\n                     var10000[2] = (new String(var10003)).intern();\n                     var10003 = \"m\\u001dT\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label384;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 77;\n                              break;\n                           case 1:\n                              var10008 = 48;\n                              break;\n                           case 2:\n                              var10008 = 116;\n                              break;\n                           case 3:\n                              var10008 = 19;\n                              break;\n                           default:\n                              var10008 = 19;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[3] = (new String(var10004)).intern();\n                  var10003 = \"a\\u00105etc\\u0010G#~$^N3\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[4] = (new String(var10003)).intern();\n                        b = var10000;\n                        a = y.a(ai.class);\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 77;\n                           break;\n                        case 1:\n                           var10008 = 48;\n                           break;\n                        case 2:\n                           var10008 = 116;\n                           break;\n                        case 3:\n                           var10008 = 19;\n                           break;\n                        default:\n                           var10008 = 19;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[4] = (new String(var10004)).intern();\n                           b = var10000;\n                           a = y.a(ai.class);\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 77;\n                     break;\n                  case 1:\n                     var10008 = 48;\n                     break;\n                  case 2:\n                     var10008 = 116;\n                     break;\n                  case 3:\n                     var10008 = 19;\n                     break;\n                  default:\n                     var10008 = 19;\n               }\n            }\n\n            while(true) {\n               while(true) {\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                     var10007 = var10004[var2];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 77;\n                           break;\n                        case 1:\n                           var10008 = 48;\n                           break;\n                        case 2:\n                           var10008 = 116;\n                           break;\n                        case 3:\n                           var10008 = 19;\n                           break;\n                        default:\n                           var10008 = 19;\n                     }\n                  } else {\n                     if (var2 <= var1) {\n                        label492: {\n                           var10000[2] = (new String(var10004)).intern();\n                           var10003 = \"m\\u001dT\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label492;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 77;\n                                    break;\n                                 case 1:\n                                    var10008 = 48;\n                                    break;\n                                 case 2:\n                                    var10008 = 116;\n                                    break;\n                                 case 3:\n                                    var10008 = 19;\n                                    break;\n                                 default:\n                                    var10008 = 19;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"a\\u00105etc\\u0010G#~$^N3\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[4] = (new String(var10003)).intern();\n                              b = var10000;\n                              a = y.a(ai.class);\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 77;\n                                 break;\n                              case 1:\n                                 var10008 = 48;\n                                 break;\n                              case 2:\n                                 var10008 = 116;\n                                 break;\n                              case 3:\n                                 var10008 = 19;\n                                 break;\n                              default:\n                                 var10008 = 19;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 b = var10000;\n                                 a = y.a(ai.class);\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                     var10007 = var10004[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 77;\n                           break;\n                        case 1:\n                           var10008 = 48;\n                           break;\n                        case 2:\n                           var10008 = 116;\n                           break;\n                        case 3:\n                           var10008 = 19;\n                           break;\n                        default:\n                           var10008 = 19;\n                     }\n                  }\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 77;\n               break;\n            case 1:\n               var10008 = 48;\n               break;\n            case 2:\n               var10008 = 116;\n               break;\n            case 3:\n               var10008 = 19;\n               break;\n            default:\n               var10008 = 19;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 77;\n                     break;\n                  case 1:\n                     var10008 = 48;\n                     break;\n                  case 2:\n                     var10008 = 116;\n                     break;\n                  case 3:\n                     var10008 = 19;\n                     break;\n                  default:\n                     var10008 = 19;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label129: {\n                     var10000[0] = (new String(var10004)).intern();\n                     var10003 = \"\\u001aQ\\u001dgz#WT|}(\\u0010\\u0019z}8D\\u0011==c\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label129;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 77;\n                              break;\n                           case 1:\n                              var10008 = 48;\n                              break;\n                           case 2:\n                              var10008 = 116;\n                              break;\n                           case 3:\n                              var10008 = 19;\n                              break;\n                           default:\n                              var10008 = 19;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[1] = (new String(var10004)).intern();\n                  var10003 = \"a\\u00105etm\\u0002@{)m\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 77;\n                           break;\n                        case 1:\n                           var10008 = 48;\n                           break;\n                        case 2:\n                           var10008 = 116;\n                           break;\n                        case 3:\n                           var10008 = 19;\n                           break;\n                        default:\n                           var10008 = 19;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        label173: {\n                           var10000[2] = (new String(var10003)).intern();\n                           var10003 = \"m\\u001dT\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label173;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 77;\n                                    break;\n                                 case 1:\n                                    var10008 = 48;\n                                    break;\n                                 case 2:\n                                    var10008 = 116;\n                                    break;\n                                 case 3:\n                                    var10008 = 19;\n                                    break;\n                                 default:\n                                    var10008 = 19;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"a\\u00105etc\\u0010G#~$^N3\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[4] = (new String(var10003)).intern();\n                              b = var10000;\n                              a = y.a(ai.class);\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 77;\n                                 break;\n                              case 1:\n                                 var10008 = 48;\n                                 break;\n                              case 2:\n                                 var10008 = 116;\n                                 break;\n                              case 3:\n                                 var10008 = 19;\n                                 break;\n                              default:\n                                 var10008 = 19;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 b = var10000;\n                                 a = y.a(ai.class);\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 77;\n                           break;\n                        case 1:\n                           var10008 = 48;\n                           break;\n                        case 2:\n                           var10008 = 116;\n                           break;\n                        case 3:\n                           var10008 = 19;\n                           break;\n                        default:\n                           var10008 = 19;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                           var10007 = var10004[var2];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 77;\n                                 break;\n                              case 1:\n                                 var10008 = 48;\n                                 break;\n                              case 2:\n                                 var10008 = 116;\n                                 break;\n                              case 3:\n                                 var10008 = 19;\n                                 break;\n                              default:\n                                 var10008 = 19;\n                           }\n                        } else {\n                           if (var2 <= var1) {\n                              label93: {\n                                 var10000[2] = (new String(var10004)).intern();\n                                 var10003 = \"m\\u001dT\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= 1) {\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var2 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label93;\n                                    }\n\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var4[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 77;\n                                          break;\n                                       case 1:\n                                          var10008 = 48;\n                                          break;\n                                       case 2:\n                                          var10008 = 116;\n                                          break;\n                                       case 3:\n                                          var10008 = 19;\n                                          break;\n                                       default:\n                                          var10008 = 19;\n                                    }\n\n                                    var4[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var2 == 0) {\n                                       var10006 = var2;\n                                       var4 = var10004;\n                                    } else {\n                                       if (var2 <= var1) {\n                                          break;\n                                       }\n\n                                       var4 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[3] = (new String(var10004)).intern();\n                              var10003 = \"a\\u00105etc\\u0010G#~$^N3\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= 1) {\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              } else {\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= var1) {\n                                    var10000[4] = (new String(var10003)).intern();\n                                    b = var10000;\n                                    a = y.a(ai.class);\n                                    return;\n                                 }\n\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              }\n\n                              while(true) {\n                                 var10007 = var4[var10006];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 77;\n                                       break;\n                                    case 1:\n                                       var10008 = 48;\n                                       break;\n                                    case 2:\n                                       var10008 = 116;\n                                       break;\n                                    case 3:\n                                       var10008 = 19;\n                                       break;\n                                    default:\n                                       var10008 = 19;\n                                 }\n\n                                 var4[var10006] = (char)(var10007 ^ var10008);\n                                 ++var1;\n                                 if (var2 == 0) {\n                                    var10006 = var2;\n                                    var4 = var10004;\n                                 } else {\n                                    if (var2 <= var1) {\n                                       var10000[4] = (new String(var10004)).intern();\n                                       b = var10000;\n                                       a = y.a(ai.class);\n                                       return;\n                                    }\n\n                                    var4 = var10004;\n                                    var10006 = var1;\n                                 }\n                              }\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 77;\n                                 break;\n                              case 1:\n                                 var10008 = 48;\n                                 break;\n                              case 2:\n                                 var10008 = 116;\n                                 break;\n                              case 3:\n                                 var10008 = 19;\n                                 break;\n                              default:\n                                 var10008 = 19;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 77;\n                     break;\n                  case 1:\n                     var10008 = 48;\n                     break;\n                  case 2:\n                     var10008 = 116;\n                     break;\n                  case 3:\n                     var10008 = 19;\n                     break;\n                  default:\n                     var10008 = 19;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a6.java",
    "content": "public class a6 {\n   public static void main(String[] param0) throws Exception {\n      // $FF: Couldn't be decompiled\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a7.java",
    "content": "import java.io.FileInputStream;\nimport java.io.InputStream;\n\npublic class a7 {\n   public static boolean b;\n   private static final String[] a;\n\n   public static void main(String[] var0) throws Exception {\n      bc var1 = new bc();\n      var1.a(a[0], new a8());\n      var1.a((InputStream)(new FileInputStream(a[1])));\n   }\n\n   static {\n      String[] var10000;\n      int var1;\n      int var2;\n      char[] var10003;\n      char[] var10004;\n      char[] var4;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label51: {\n         var10000 = new String[2];\n         var10003 = \"Z7wN\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= 1) {\n            var4 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= var1) {\n               break label51;\n            }\n\n            var4 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var4[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 52;\n                  break;\n               case 1:\n                  var10008 = 88;\n                  break;\n               case 2:\n                  var10008 = 19;\n                  break;\n               case 3:\n                  var10008 = 43;\n                  break;\n               default:\n                  var10008 = 10;\n            }\n\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"G*p\\u0004oL9~[fQ+<_oG,=SgX\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var2 = var10005;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            var10000[1] = (new String(var10003)).intern();\n            a = var10000;\n            return;\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n      }\n\n      while(true) {\n         var10007 = var4[var10006];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 52;\n               break;\n            case 1:\n               var10008 = 88;\n               break;\n            case 2:\n               var10008 = 19;\n               break;\n            case 3:\n               var10008 = 43;\n               break;\n            default:\n               var10008 = 10;\n         }\n\n         var4[var10006] = (char)(var10007 ^ var10008);\n         ++var1;\n         if (var2 == 0) {\n            var10006 = var2;\n            var4 = var10004;\n         } else {\n            if (var2 <= var1) {\n               var10000[1] = (new String(var10004)).intern();\n               a = var10000;\n               return;\n            }\n\n            var4 = var10004;\n            var10006 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a8.java",
    "content": "final class a8 implements ay {\n   private static final String[] a;\n\n   public void a(a0 param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   static {\n      String[] var10000;\n      int var1;\n      int var2;\n      char[] var10003;\n      char[] var10004;\n      char[] var4;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label115: {\n         var10000 = new String[4];\n         var10003 = \"(\\u000f7O\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= 1) {\n            var4 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= var1) {\n               break label115;\n            }\n\n            var4 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var4[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 70;\n                  break;\n               case 1:\n                  var10008 = 110;\n                  break;\n               case 2:\n                  var10008 = 90;\n                  break;\n               case 3:\n                  var10008 = 42;\n                  break;\n               default:\n                  var10008 = 64;\n            }\n\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"4\\u000b)E54\\r?Yo4\\u000b)E54\\r?q\\u00002\\u0017*O}a\\u001a?Y4a3\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var2 = var10005;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 70;\n               break;\n            case 1:\n               var10008 = 110;\n               break;\n            case 2:\n               var10008 = 90;\n               break;\n            case 3:\n               var10008 = 42;\n               break;\n            default:\n               var10008 = 64;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label158: {\n               var10000[1] = (new String(var10003)).intern();\n               var10003 = \"6\\u001c3I%\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label158;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 70;\n                        break;\n                     case 1:\n                        var10008 = 110;\n                        break;\n                     case 2:\n                        var10008 = 90;\n                        break;\n                     case 3:\n                        var10008 = 42;\n                        break;\n                     default:\n                        var10008 = 64;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[2] = (new String(var10004)).intern();\n            var10003 = \"4\\u000b)E54\\r?Yo4\\u000b)E54\\r?q\\u00002\\u0017*O}a\\u001a?Y4a3\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  var10000[3] = (new String(var10003)).intern();\n                  a = var10000;\n                  return;\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n            }\n\n            while(true) {\n               var10007 = var4[var10006];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 70;\n                     break;\n                  case 1:\n                     var10008 = 110;\n                     break;\n                  case 2:\n                     var10008 = 90;\n                     break;\n                  case 3:\n                     var10008 = 42;\n                     break;\n                  default:\n                     var10008 = 64;\n               }\n\n               var4[var10006] = (char)(var10007 ^ var10008);\n               ++var1;\n               if (var2 == 0) {\n                  var10006 = var2;\n                  var4 = var10004;\n               } else {\n                  if (var2 <= var1) {\n                     var10000[3] = (new String(var10004)).intern();\n                     a = var10000;\n                     return;\n                  }\n\n                  var4 = var10004;\n                  var10006 = var1;\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 70;\n               break;\n            case 1:\n               var10008 = 110;\n               break;\n            case 2:\n               var10008 = 90;\n               break;\n            case 3:\n               var10008 = 42;\n               break;\n            default:\n               var10008 = 64;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 70;\n                     break;\n                  case 1:\n                     var10008 = 110;\n                     break;\n                  case 2:\n                     var10008 = 90;\n                     break;\n                  case 3:\n                     var10008 = 42;\n                     break;\n                  default:\n                     var10008 = 64;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label79: {\n                     var10000[1] = (new String(var10004)).intern();\n                     var10003 = \"6\\u001c3I%\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label79;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 70;\n                              break;\n                           case 1:\n                              var10008 = 110;\n                              break;\n                           case 2:\n                              var10008 = 90;\n                              break;\n                           case 3:\n                              var10008 = 42;\n                              break;\n                           default:\n                              var10008 = 64;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[2] = (new String(var10004)).intern();\n                  var10003 = \"4\\u000b)E54\\r?Yo4\\u000b)E54\\r?q\\u00002\\u0017*O}a\\u001a?Y4a3\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[3] = (new String(var10003)).intern();\n                        a = var10000;\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 70;\n                           break;\n                        case 1:\n                           var10008 = 110;\n                           break;\n                        case 2:\n                           var10008 = 90;\n                           break;\n                        case 3:\n                           var10008 = 42;\n                           break;\n                        default:\n                           var10008 = 64;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[3] = (new String(var10004)).intern();\n                           a = var10000;\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 70;\n                     break;\n                  case 1:\n                     var10008 = 110;\n                     break;\n                  case 2:\n                     var10008 = 90;\n                     break;\n                  case 3:\n                     var10008 = 42;\n                     break;\n                  default:\n                     var10008 = 64;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a9.java",
    "content": "public class a9 extends RuntimeException {\n   private static final long serialVersionUID = 1180433275280350911L;\n\n   public a9(String var1, Throwable var2) {\n      super(var1, var2);\n   }\n\n   public a9(Throwable var1) {\n      super(var1);\n   }\n\n   public a9(String var1) {\n      super(var1);\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/a_.java",
    "content": "class a_ extends RuntimeException {\n   private static final long serialVersionUID = -7454219131982518216L;\n   final bc a;\n\n   a_(bc var1) {\n      this.a = var1;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/aa.java",
    "content": "import java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.TYPE})\npublic @interface aa {\n   Class<?>[] a();\n}\n"
  },
  {
    "path": "testData/obfuscated/ab.java",
    "content": "import java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\npublic class ab implements u {\n   private List<Object> a = new ArrayList();\n\n   public void a(Class<?> param1) throws Exception {\n      // $FF: Couldn't be decompiled\n   }\n\n   public void a() throws Exception {\n      int var3 = y.d;\n      Iterator var1 = this.a.iterator();\n\n      while(true) {\n         if (var1.hasNext()) {\n            Object var2 = var1.next();\n\n            try {\n               v.a(var2);\n               if (var3 != 0) {\n                  break;\n               }\n\n               if (var3 == 0) {\n                  continue;\n               }\n            } catch (Exception var5) {\n               throw var5;\n            }\n\n            int var4 = ap.c;\n            ++var4;\n            ap.c = var4;\n         }\n\n         this.a.clear();\n         break;\n      }\n\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ac.java",
    "content": "public interface ac {\n   void a() throws Exception;\n}\n"
  },
  {
    "path": "testData/obfuscated/ad.java",
    "content": "public interface ad {\n   String a();\n}\n"
  },
  {
    "path": "testData/obfuscated/ae.java",
    "content": "import java.text.DateFormat;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Timer;\nimport java.util.concurrent.locks.ReentrantLock;\n\n@aa(\n   a = {ad.class}\n)\npublic class ae implements ad {\n   @x(\n      a = ac.class\n   )\n   private List<ac> a;\n   private long b = 0L;\n   private Timer c;\n   private ReentrantLock d = new ReentrantLock();\n   public static boolean e;\n\n   public ae() {\n      this.a();\n   }\n\n   public void a() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public void b() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public String a() {\n      try {\n         if (this.b == 0L) {\n            return \"-\";\n         }\n      } catch (a_ var1) {\n         throw var1;\n      }\n\n      return DateFormat.getDateTimeInstance().format(new Date(this.b));\n   }\n\n   static List a(ae var0) {\n      return var0.a;\n   }\n\n   static long a(ae var0, long var1) {\n      return var0.b = var1;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/af.java",
    "content": "// $FF: synthetic class\nclass af {\n}\n"
  },
  {
    "path": "testData/obfuscated/ag.java",
    "content": "public class ag {\n   private long a = 0L;\n   private long[] b = new long[100];\n   private int c = 0;\n   private int d = 0;\n\n   public void a(long param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public double a() {\n      try {\n         if (this.d == 0) {\n            return 0.0;\n         }\n      } catch (a_ var4) {\n         throw var4;\n      }\n\n      double var1 = 0.0;\n\n      for(int var3 = 0; var3 <= this.d; ++var3) {\n         var1 += (double)this.b[var3];\n      }\n\n      return var1 / (double)this.d;\n   }\n\n   public long b() {\n      return this.a;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ah.java",
    "content": "import java.util.concurrent.TimeUnit;\n\npublic class ah {\n   private volatile long a = -1L;\n   private volatile long b = 0L;\n\n   public void a() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public double a(TimeUnit var1) {\n      return (double)(this.b / this.b(var1));\n   }\n\n   public long b() {\n      return this.b;\n   }\n\n   public long b(TimeUnit var1) {\n      long var2 = System.currentTimeMillis() - this.a;\n      return TimeUnit.MILLISECONDS.convert(var2, var1);\n   }\n\n   public void c() {\n      this.a = System.currentTimeMillis();\n      this.b = 0L;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ai.java",
    "content": "import java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@aa(\n   a = {ac.class, ai.class}\n)\npublic class ai implements ac {\n   @x(\n      a = am.class\n   )\n   private List<am> a;\n   private static Map<ak, al> b = Collections.synchronizedMap(new LinkedHashMap());\n\n   public void a() throws Exception {\n      boolean var3 = an.k;\n      Iterator var1 = this.a.iterator();\n\n      while(var1.hasNext()) {\n         am var2 = (am)var1.next();\n         var2.a(new j(this));\n         if (var3) {\n            break;\n         }\n      }\n\n   }\n\n   public Collection<al> a() {\n      return b.values();\n   }\n\n   static Map b() {\n      return b;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/aj.java",
    "content": "import java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n@aa(\n   a = {am.class}\n)\npublic class aj implements am {\n   private static final Pattern a;\n   private Map<String, Long> b;\n   private long c;\n   private Map<String, n<Long, Long>> d;\n   private long e;\n   private static final long f = 10L;\n   private static final Pattern g;\n   private static final String[] h;\n\n   private Map<String, Long> a() throws IOException {\n      LinkedHashMap var1 = new LinkedHashMap();\n      File var2 = new File(h[2]);\n\n      try {\n         if (!var2.exists()) {\n            return var1;\n         }\n      } catch (IOException var12) {\n         throw var12;\n      }\n\n      BufferedReader var3 = new BufferedReader(new FileReader(var2));\n\n      try {\n         for(String var4 = var3.readLine(); var4 != null; var4 = var3.readLine()) {\n            Matcher var5 = a.matcher(var4);\n\n            try {\n               if (var5.matches()) {\n                  var1.put(var5.group(1), Long.parseLong(var5.group(2)));\n               }\n            } catch (IOException var10) {\n               throw var10;\n            }\n         }\n      } finally {\n         var3.close();\n      }\n\n      return var1;\n   }\n\n   private Map<String, n<Long, Long>> b() throws IOException {\n      LinkedHashMap var1 = new LinkedHashMap();\n      File var2 = new File(h[0]);\n\n      try {\n         if (!var2.exists()) {\n            return var1;\n         }\n      } catch (IOException var12) {\n         throw var12;\n      }\n\n      BufferedReader var3 = new BufferedReader(new FileReader(var2));\n\n      try {\n         for(String var4 = var3.readLine(); var4 != null; var4 = var3.readLine()) {\n            Matcher var5 = g.matcher(var4);\n\n            try {\n               if (var5.matches()) {\n                  var1.put(var5.group(1), new n(Long.parseLong(var5.group(2)), Long.parseLong(var5.group(3))));\n               }\n            } catch (IOException var10) {\n               throw var10;\n            }\n         }\n      } finally {\n         var3.close();\n      }\n\n      return var1;\n   }\n\n   public void a(k<ak> var1) {\n      this.b(var1);\n      this.c(var1);\n   }\n\n   protected void b(k<ak> var1) {\n      try {\n         Map var2 = this.a();\n         long var3 = System.currentTimeMillis() - this.c;\n         this.c = System.currentTimeMillis();\n         if (this.b != null) {\n            Iterator var5 = var2.entrySet().iterator();\n\n            while(var5.hasNext()) {\n               Map.Entry var6 = (Map.Entry)var5.next();\n               Long var7 = (Long)this.b.get(var6.getKey());\n               if (var7 != null) {\n                  double var8 = (double)(((Long)var6.getValue() - var7) * 10L) / (double)var3;\n                  var1.a((Object)(new ar(h[1], (String)var6.getKey(), \"%\", var8 * 100.0)));\n               }\n            }\n         }\n\n         this.b = var2;\n      } catch (IOException var10) {\n         var10.printStackTrace();\n      }\n\n   }\n\n   protected void c(k<ak> param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   static {\n      String[] var10000 = new String[9];\n      char[] var10003 = \"h!\\\"+hh597`4%10x\".toCharArray();\n      int var10005 = var10003.length;\n      int var1 = 0;\n      char[] var10004 = var10003;\n      int var6 = var10005;\n      char[] var2;\n      int var3;\n      char[] var5;\n      char[] var8;\n      char var9;\n      byte var10;\n      char[] var10001;\n      int var10002;\n      int var10006;\n      char var10007;\n      byte var10008;\n      if (var10005 <= 1) {\n         var8 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 71;\n               break;\n            case 1:\n               var10008 = 81;\n               break;\n            case 2:\n               var10008 = 80;\n               break;\n            case 3:\n               var10008 = 68;\n               break;\n            default:\n               var10008 = 11;\n         }\n      } else {\n         var10004 = var10003;\n         var6 = var10005;\n         if (var10005 <= var1) {\n            label3117: {\n               var10000[0] = (new String(var10003)).intern();\n               var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var6 = var10005;\n               if (var10005 <= 1) {\n                  var8 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var6 = var10005;\n                  if (var10005 <= var1) {\n                     break label3117;\n                  }\n\n                  var8 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var8[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 71;\n                        break;\n                     case 1:\n                        var10008 = 81;\n                        break;\n                     case 2:\n                        var10008 = 80;\n                        break;\n                     case 3:\n                        var10008 = 68;\n                        break;\n                     default:\n                        var10008 = 11;\n                  }\n\n                  var8[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var6 == 0) {\n                     var10006 = var6;\n                     var8 = var10004;\n                  } else {\n                     if (var6 <= var1) {\n                        break;\n                     }\n\n                     var8 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[1] = (new String(var10004)).intern();\n            var10003 = \"h!\\\"+hh\\\"$%\\u007f\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var6 = var10005;\n            if (var10005 <= 1) {\n               var8 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 71;\n                     break;\n                  case 1:\n                     var10008 = 81;\n                     break;\n                  case 2:\n                     var10008 = 80;\n                     break;\n                  case 3:\n                     var10008 = 68;\n                     break;\n                  default:\n                     var10008 = 11;\n               }\n            } else {\n               var10004 = var10003;\n               var6 = var10005;\n               if (var10005 <= var1) {\n                  label3185: {\n                     var10000[2] = (new String(var10003)).intern();\n                     var10003 = \"j#5%o4\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var6 = var10005;\n                     if (var10005 <= 1) {\n                        var8 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= var1) {\n                           break label3185;\n                        }\n\n                        var8 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var8[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 71;\n                              break;\n                           case 1:\n                              var10008 = 81;\n                              break;\n                           case 2:\n                              var10008 = 80;\n                              break;\n                           case 3:\n                              var10008 = 68;\n                              break;\n                           default:\n                              var10008 = 11;\n                        }\n\n                        var8[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var6 == 0) {\n                           var10006 = var6;\n                           var8 = var10004;\n                        } else {\n                           if (var6 <= var1) {\n                              break;\n                           }\n\n                           var8 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[3] = (new String(var10004)).intern();\n                  var10003 = \"v~#\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var6 = var10005;\n                  if (var10005 <= 1) {\n                     var8 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 71;\n                           break;\n                        case 1:\n                           var10008 = 81;\n                           break;\n                        case 2:\n                           var10008 = 80;\n                           break;\n                        case 3:\n                           var10008 = 68;\n                           break;\n                        default:\n                           var10008 = 11;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var6 = var10005;\n                     if (var10005 <= var1) {\n                        label3253: {\n                           var10000[4] = (new String(var10003)).intern();\n                           var10003 = \"j&\\\"-\\u007f\\\"\\\"\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= 1) {\n                              var8 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= var1) {\n                                 break label3253;\n                              }\n\n                              var8 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var8[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 71;\n                                    break;\n                                 case 1:\n                                    var10008 = 81;\n                                    break;\n                                 case 2:\n                                    var10008 = 80;\n                                    break;\n                                 case 3:\n                                    var10008 = 68;\n                                    break;\n                                 default:\n                                    var10008 = 11;\n                              }\n\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                              } else {\n                                 if (var6 <= var1) {\n                                    break;\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[5] = (new String(var10004)).intern();\n                        var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= 1) {\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        } else {\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= var1) {\n                              label3321: {\n                                 var10000[6] = (new String(var10003)).intern();\n                                 var10003 = \"v~#\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label3321;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 71;\n                                          break;\n                                       case 1:\n                                          var10008 = 81;\n                                          break;\n                                       case 2:\n                                          var10008 = 80;\n                                          break;\n                                       case 3:\n                                          var10008 = 68;\n                                          break;\n                                       default:\n                                          var10008 = 11;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[7] = (new String(var10004)).intern();\n                              var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= 1) {\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              } else {\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= var1) {\n                                    label3389: {\n                                       var10000[8] = (new String(var10003)).intern();\n                                       h = var10000;\n                                       var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                       var10002 = var2.length;\n                                       var1 = 0;\n                                       var10001 = var2;\n                                       var3 = var10002;\n                                       if (var10002 <= 1) {\n                                          var5 = var2;\n                                          var6 = var1;\n                                       } else {\n                                          var10001 = var2;\n                                          var3 = var10002;\n                                          if (var10002 <= var1) {\n                                             break label3389;\n                                          }\n\n                                          var5 = var2;\n                                          var6 = var1;\n                                       }\n\n                                       while(true) {\n                                          var9 = var5[var6];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10 = 71;\n                                                break;\n                                             case 1:\n                                                var10 = 81;\n                                                break;\n                                             case 2:\n                                                var10 = 80;\n                                                break;\n                                             case 3:\n                                                var10 = 68;\n                                                break;\n                                             default:\n                                                var10 = 11;\n                                          }\n\n                                          var5[var6] = (char)(var9 ^ var10);\n                                          ++var1;\n                                          if (var3 == 0) {\n                                             var6 = var3;\n                                             var5 = var10001;\n                                          } else {\n                                             if (var3 <= var1) {\n                                                break;\n                                             }\n\n                                             var5 = var10001;\n                                             var6 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    a = Pattern.compile((new String(var10001)).intern());\n                                    var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                    var10002 = var2.length;\n                                    var1 = 0;\n                                    var10001 = var2;\n                                    var3 = var10002;\n                                    if (var10002 <= 1) {\n                                       var5 = var2;\n                                       var6 = var1;\n                                    } else {\n                                       var10001 = var2;\n                                       var3 = var10002;\n                                       if (var10002 <= var1) {\n                                          g = Pattern.compile((new String(var2)).intern());\n                                          return;\n                                       }\n\n                                       var5 = var2;\n                                       var6 = var1;\n                                    }\n\n                                    while(true) {\n                                       var9 = var5[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10 = 71;\n                                             break;\n                                          case 1:\n                                             var10 = 81;\n                                             break;\n                                          case 2:\n                                             var10 = 80;\n                                             break;\n                                          case 3:\n                                             var10 = 68;\n                                             break;\n                                          default:\n                                             var10 = 11;\n                                       }\n\n                                       var5[var6] = (char)(var9 ^ var10);\n                                       ++var1;\n                                       if (var3 == 0) {\n                                          var6 = var3;\n                                          var5 = var10001;\n                                       } else {\n                                          if (var3 <= var1) {\n                                             g = Pattern.compile((new String(var10001)).intern());\n                                             return;\n                                          }\n\n                                          var5 = var10001;\n                                          var6 = var1;\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              }\n\n                              while(true) {\n                                 while(true) {\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                       var10007 = var10004[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       if (var6 <= var1) {\n                                          label3497: {\n                                             var10000[8] = (new String(var10004)).intern();\n                                             h = var10000;\n                                             var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                             var10002 = var2.length;\n                                             var1 = 0;\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= 1) {\n                                                var5 = var2;\n                                                var6 = var1;\n                                             } else {\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= var1) {\n                                                   break label3497;\n                                                }\n\n                                                var5 = var2;\n                                                var6 = var1;\n                                             }\n\n                                             while(true) {\n                                                var9 = var5[var6];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10 = 68;\n                                                      break;\n                                                   default:\n                                                      var10 = 11;\n                                                }\n\n                                                var5[var6] = (char)(var9 ^ var10);\n                                                ++var1;\n                                                if (var3 == 0) {\n                                                   var6 = var3;\n                                                   var5 = var10001;\n                                                } else {\n                                                   if (var3 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var5 = var10001;\n                                                   var6 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          a = Pattern.compile((new String(var10001)).intern());\n                                          var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                          var10002 = var2.length;\n                                          var1 = 0;\n                                          var10001 = var2;\n                                          var3 = var10002;\n                                          if (var10002 <= 1) {\n                                             var5 = var2;\n                                             var6 = var1;\n                                          } else {\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= var1) {\n                                                g = Pattern.compile((new String(var2)).intern());\n                                                return;\n                                             }\n\n                                             var5 = var2;\n                                             var6 = var1;\n                                          }\n\n                                          while(true) {\n                                             var9 = var5[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10 = 68;\n                                                   break;\n                                                default:\n                                                   var10 = 11;\n                                             }\n\n                                             var5[var6] = (char)(var9 ^ var10);\n                                             ++var1;\n                                             if (var3 == 0) {\n                                                var6 = var3;\n                                                var5 = var10001;\n                                             } else {\n                                                if (var3 <= var1) {\n                                                   g = Pattern.compile((new String(var10001)).intern());\n                                                   return;\n                                                }\n\n                                                var5 = var10001;\n                                                var6 = var1;\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                       var10007 = var10004[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n                                 }\n                              }\n                           }\n\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        }\n\n                        while(true) {\n                           while(true) {\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                                 var10007 = var10004[var6];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              } else {\n                                 if (var6 <= var1) {\n                                    label3632: {\n                                       var10000[6] = (new String(var10004)).intern();\n                                       var10003 = \"v~#\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label3632;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 71;\n                                                break;\n                                             case 1:\n                                                var10008 = 81;\n                                                break;\n                                             case 2:\n                                                var10008 = 80;\n                                                break;\n                                             case 3:\n                                                var10008 = 68;\n                                                break;\n                                             default:\n                                                var10008 = 11;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[7] = (new String(var10004)).intern();\n                                    var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                    var10005 = var10003.length;\n                                    var1 = 0;\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= 1) {\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= var1) {\n                                          label3700: {\n                                             var10000[8] = (new String(var10003)).intern();\n                                             h = var10000;\n                                             var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                             var10002 = var2.length;\n                                             var1 = 0;\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= 1) {\n                                                var5 = var2;\n                                                var6 = var1;\n                                             } else {\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= var1) {\n                                                   break label3700;\n                                                }\n\n                                                var5 = var2;\n                                                var6 = var1;\n                                             }\n\n                                             while(true) {\n                                                var9 = var5[var6];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10 = 68;\n                                                      break;\n                                                   default:\n                                                      var10 = 11;\n                                                }\n\n                                                var5[var6] = (char)(var9 ^ var10);\n                                                ++var1;\n                                                if (var3 == 0) {\n                                                   var6 = var3;\n                                                   var5 = var10001;\n                                                } else {\n                                                   if (var3 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var5 = var10001;\n                                                   var6 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          a = Pattern.compile((new String(var10001)).intern());\n                                          var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                          var10002 = var2.length;\n                                          var1 = 0;\n                                          var10001 = var2;\n                                          var3 = var10002;\n                                          if (var10002 <= 1) {\n                                             var5 = var2;\n                                             var6 = var1;\n                                          } else {\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= var1) {\n                                                g = Pattern.compile((new String(var2)).intern());\n                                                return;\n                                             }\n\n                                             var5 = var2;\n                                             var6 = var1;\n                                          }\n\n                                          while(true) {\n                                             var9 = var5[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10 = 68;\n                                                   break;\n                                                default:\n                                                   var10 = 11;\n                                             }\n\n                                             var5[var6] = (char)(var9 ^ var10);\n                                             ++var1;\n                                             if (var3 == 0) {\n                                                var6 = var3;\n                                                var5 = var10001;\n                                             } else {\n                                                if (var3 <= var1) {\n                                                   g = Pattern.compile((new String(var10001)).intern());\n                                                   return;\n                                                }\n\n                                                var5 = var10001;\n                                                var6 = var1;\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n\n                                    while(true) {\n                                       while(true) {\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                             var10007 = var10004[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             if (var6 <= var1) {\n                                                label3808: {\n                                                   var10000[8] = (new String(var10004)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label3808;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                             var10007 = var10004[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                                 var10007 = var10004[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              }\n                           }\n                        }\n                     }\n\n                     var8 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 71;\n                           break;\n                        case 1:\n                           var10008 = 81;\n                           break;\n                        case 2:\n                           var10008 = 80;\n                           break;\n                        case 3:\n                           var10008 = 68;\n                           break;\n                        default:\n                           var10008 = 11;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var8[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var6 == 0) {\n                           var10006 = var6;\n                           var8 = var10004;\n                           var10007 = var10004[var6];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        } else {\n                           if (var6 <= var1) {\n                              label3970: {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 var10003 = \"j&\\\"-\\u007f\\\"\\\"\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label3970;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 71;\n                                          break;\n                                       case 1:\n                                          var10008 = 81;\n                                          break;\n                                       case 2:\n                                          var10008 = 80;\n                                          break;\n                                       case 3:\n                                          var10008 = 68;\n                                          break;\n                                       default:\n                                          var10008 = 11;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[5] = (new String(var10004)).intern();\n                              var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= 1) {\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              } else {\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= var1) {\n                                    label4038: {\n                                       var10000[6] = (new String(var10003)).intern();\n                                       var10003 = \"v~#\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label4038;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 71;\n                                                break;\n                                             case 1:\n                                                var10008 = 81;\n                                                break;\n                                             case 2:\n                                                var10008 = 80;\n                                                break;\n                                             case 3:\n                                                var10008 = 68;\n                                                break;\n                                             default:\n                                                var10008 = 11;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[7] = (new String(var10004)).intern();\n                                    var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                    var10005 = var10003.length;\n                                    var1 = 0;\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= 1) {\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= var1) {\n                                          label4106: {\n                                             var10000[8] = (new String(var10003)).intern();\n                                             h = var10000;\n                                             var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                             var10002 = var2.length;\n                                             var1 = 0;\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= 1) {\n                                                var5 = var2;\n                                                var6 = var1;\n                                             } else {\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= var1) {\n                                                   break label4106;\n                                                }\n\n                                                var5 = var2;\n                                                var6 = var1;\n                                             }\n\n                                             while(true) {\n                                                var9 = var5[var6];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10 = 68;\n                                                      break;\n                                                   default:\n                                                      var10 = 11;\n                                                }\n\n                                                var5[var6] = (char)(var9 ^ var10);\n                                                ++var1;\n                                                if (var3 == 0) {\n                                                   var6 = var3;\n                                                   var5 = var10001;\n                                                } else {\n                                                   if (var3 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var5 = var10001;\n                                                   var6 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          a = Pattern.compile((new String(var10001)).intern());\n                                          var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                          var10002 = var2.length;\n                                          var1 = 0;\n                                          var10001 = var2;\n                                          var3 = var10002;\n                                          if (var10002 <= 1) {\n                                             var5 = var2;\n                                             var6 = var1;\n                                          } else {\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= var1) {\n                                                g = Pattern.compile((new String(var2)).intern());\n                                                return;\n                                             }\n\n                                             var5 = var2;\n                                             var6 = var1;\n                                          }\n\n                                          while(true) {\n                                             var9 = var5[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10 = 68;\n                                                   break;\n                                                default:\n                                                   var10 = 11;\n                                             }\n\n                                             var5[var6] = (char)(var9 ^ var10);\n                                             ++var1;\n                                             if (var3 == 0) {\n                                                var6 = var3;\n                                                var5 = var10001;\n                                             } else {\n                                                if (var3 <= var1) {\n                                                   g = Pattern.compile((new String(var10001)).intern());\n                                                   return;\n                                                }\n\n                                                var5 = var10001;\n                                                var6 = var1;\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n\n                                    while(true) {\n                                       while(true) {\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                             var10007 = var10004[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             if (var6 <= var1) {\n                                                label4214: {\n                                                   var10000[8] = (new String(var10004)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label4214;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                             var10007 = var10004[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              }\n\n                              while(true) {\n                                 while(true) {\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                       var10007 = var10004[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       if (var6 <= var1) {\n                                          label4349: {\n                                             var10000[6] = (new String(var10004)).intern();\n                                             var10003 = \"v~#\".toCharArray();\n                                             var10005 = var10003.length;\n                                             var1 = 0;\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= 1) {\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             } else {\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= var1) {\n                                                   break label4349;\n                                                }\n\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             }\n\n                                             while(true) {\n                                                var10007 = var8[var10006];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10008 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10008 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10008 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10008 = 68;\n                                                      break;\n                                                   default:\n                                                      var10008 = 11;\n                                                }\n\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          var10000[7] = (new String(var10004)).intern();\n                                          var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                          var10005 = var10003.length;\n                                          var1 = 0;\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= 1) {\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= var1) {\n                                                label4417: {\n                                                   var10000[8] = (new String(var10003)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label4417;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n\n                                          while(true) {\n                                             while(true) {\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                   var10007 = var10004[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      label4525: {\n                                                         var10000[8] = (new String(var10004)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label4525;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                   var10007 = var10004[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                       var10007 = var10004[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n                                 }\n                              }\n                           }\n\n                           var8 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var8 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 71;\n                     break;\n                  case 1:\n                     var10008 = 81;\n                     break;\n                  case 2:\n                     var10008 = 80;\n                     break;\n                  case 3:\n                     var10008 = 68;\n                     break;\n                  default:\n                     var10008 = 11;\n               }\n            }\n\n            while(true) {\n               while(true) {\n                  var8[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var6 == 0) {\n                     var10006 = var6;\n                     var8 = var10004;\n                     var10007 = var10004[var6];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 71;\n                           break;\n                        case 1:\n                           var10008 = 81;\n                           break;\n                        case 2:\n                           var10008 = 80;\n                           break;\n                        case 3:\n                           var10008 = 68;\n                           break;\n                        default:\n                           var10008 = 11;\n                     }\n                  } else {\n                     if (var6 <= var1) {\n                        label4714: {\n                           var10000[2] = (new String(var10004)).intern();\n                           var10003 = \"j#5%o4\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= 1) {\n                              var8 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= var1) {\n                                 break label4714;\n                              }\n\n                              var8 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var8[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 71;\n                                    break;\n                                 case 1:\n                                    var10008 = 81;\n                                    break;\n                                 case 2:\n                                    var10008 = 80;\n                                    break;\n                                 case 3:\n                                    var10008 = 68;\n                                    break;\n                                 default:\n                                    var10008 = 11;\n                              }\n\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                              } else {\n                                 if (var6 <= var1) {\n                                    break;\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"v~#\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= 1) {\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        } else {\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= var1) {\n                              label4782: {\n                                 var10000[4] = (new String(var10003)).intern();\n                                 var10003 = \"j&\\\"-\\u007f\\\"\\\"\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label4782;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 71;\n                                          break;\n                                       case 1:\n                                          var10008 = 81;\n                                          break;\n                                       case 2:\n                                          var10008 = 80;\n                                          break;\n                                       case 3:\n                                          var10008 = 68;\n                                          break;\n                                       default:\n                                          var10008 = 11;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[5] = (new String(var10004)).intern();\n                              var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= 1) {\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              } else {\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= var1) {\n                                    label4850: {\n                                       var10000[6] = (new String(var10003)).intern();\n                                       var10003 = \"v~#\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label4850;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 71;\n                                                break;\n                                             case 1:\n                                                var10008 = 81;\n                                                break;\n                                             case 2:\n                                                var10008 = 80;\n                                                break;\n                                             case 3:\n                                                var10008 = 68;\n                                                break;\n                                             default:\n                                                var10008 = 11;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[7] = (new String(var10004)).intern();\n                                    var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                    var10005 = var10003.length;\n                                    var1 = 0;\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= 1) {\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= var1) {\n                                          label4918: {\n                                             var10000[8] = (new String(var10003)).intern();\n                                             h = var10000;\n                                             var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                             var10002 = var2.length;\n                                             var1 = 0;\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= 1) {\n                                                var5 = var2;\n                                                var6 = var1;\n                                             } else {\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= var1) {\n                                                   break label4918;\n                                                }\n\n                                                var5 = var2;\n                                                var6 = var1;\n                                             }\n\n                                             while(true) {\n                                                var9 = var5[var6];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10 = 68;\n                                                      break;\n                                                   default:\n                                                      var10 = 11;\n                                                }\n\n                                                var5[var6] = (char)(var9 ^ var10);\n                                                ++var1;\n                                                if (var3 == 0) {\n                                                   var6 = var3;\n                                                   var5 = var10001;\n                                                } else {\n                                                   if (var3 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var5 = var10001;\n                                                   var6 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          a = Pattern.compile((new String(var10001)).intern());\n                                          var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                          var10002 = var2.length;\n                                          var1 = 0;\n                                          var10001 = var2;\n                                          var3 = var10002;\n                                          if (var10002 <= 1) {\n                                             var5 = var2;\n                                             var6 = var1;\n                                          } else {\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= var1) {\n                                                g = Pattern.compile((new String(var2)).intern());\n                                                return;\n                                             }\n\n                                             var5 = var2;\n                                             var6 = var1;\n                                          }\n\n                                          while(true) {\n                                             var9 = var5[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10 = 68;\n                                                   break;\n                                                default:\n                                                   var10 = 11;\n                                             }\n\n                                             var5[var6] = (char)(var9 ^ var10);\n                                             ++var1;\n                                             if (var3 == 0) {\n                                                var6 = var3;\n                                                var5 = var10001;\n                                             } else {\n                                                if (var3 <= var1) {\n                                                   g = Pattern.compile((new String(var10001)).intern());\n                                                   return;\n                                                }\n\n                                                var5 = var10001;\n                                                var6 = var1;\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n\n                                    while(true) {\n                                       while(true) {\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                             var10007 = var10004[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             if (var6 <= var1) {\n                                                label5026: {\n                                                   var10000[8] = (new String(var10004)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label5026;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                             var10007 = var10004[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              }\n\n                              while(true) {\n                                 while(true) {\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                       var10007 = var10004[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       if (var6 <= var1) {\n                                          label5161: {\n                                             var10000[6] = (new String(var10004)).intern();\n                                             var10003 = \"v~#\".toCharArray();\n                                             var10005 = var10003.length;\n                                             var1 = 0;\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= 1) {\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             } else {\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= var1) {\n                                                   break label5161;\n                                                }\n\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             }\n\n                                             while(true) {\n                                                var10007 = var8[var10006];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10008 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10008 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10008 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10008 = 68;\n                                                      break;\n                                                   default:\n                                                      var10008 = 11;\n                                                }\n\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          var10000[7] = (new String(var10004)).intern();\n                                          var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                          var10005 = var10003.length;\n                                          var1 = 0;\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= 1) {\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= var1) {\n                                                label5229: {\n                                                   var10000[8] = (new String(var10003)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label5229;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n\n                                          while(true) {\n                                             while(true) {\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                   var10007 = var10004[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      label5337: {\n                                                         var10000[8] = (new String(var10004)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label5337;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                   var10007 = var10004[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                       var10007 = var10004[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n                                 }\n                              }\n                           }\n\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        }\n\n                        while(true) {\n                           while(true) {\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                                 var10007 = var10004[var6];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              } else {\n                                 if (var6 <= var1) {\n                                    label5499: {\n                                       var10000[4] = (new String(var10004)).intern();\n                                       var10003 = \"j&\\\"-\\u007f\\\"\\\"\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label5499;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 71;\n                                                break;\n                                             case 1:\n                                                var10008 = 81;\n                                                break;\n                                             case 2:\n                                                var10008 = 80;\n                                                break;\n                                             case 3:\n                                                var10008 = 68;\n                                                break;\n                                             default:\n                                                var10008 = 11;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[5] = (new String(var10004)).intern();\n                                    var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                    var10005 = var10003.length;\n                                    var1 = 0;\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= 1) {\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= var1) {\n                                          label5567: {\n                                             var10000[6] = (new String(var10003)).intern();\n                                             var10003 = \"v~#\".toCharArray();\n                                             var10005 = var10003.length;\n                                             var1 = 0;\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= 1) {\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             } else {\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= var1) {\n                                                   break label5567;\n                                                }\n\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             }\n\n                                             while(true) {\n                                                var10007 = var8[var10006];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10008 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10008 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10008 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10008 = 68;\n                                                      break;\n                                                   default:\n                                                      var10008 = 11;\n                                                }\n\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          var10000[7] = (new String(var10004)).intern();\n                                          var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                          var10005 = var10003.length;\n                                          var1 = 0;\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= 1) {\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= var1) {\n                                                label5635: {\n                                                   var10000[8] = (new String(var10003)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label5635;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n\n                                          while(true) {\n                                             while(true) {\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                   var10007 = var10004[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      label5743: {\n                                                         var10000[8] = (new String(var10004)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label5743;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                   var10007 = var10004[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n\n                                    while(true) {\n                                       while(true) {\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                             var10007 = var10004[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             if (var6 <= var1) {\n                                                label5878: {\n                                                   var10000[6] = (new String(var10004)).intern();\n                                                   var10003 = \"v~#\".toCharArray();\n                                                   var10005 = var10003.length;\n                                                   var1 = 0;\n                                                   var10004 = var10003;\n                                                   var6 = var10005;\n                                                   if (var10005 <= 1) {\n                                                      var8 = var10003;\n                                                      var10006 = var1;\n                                                   } else {\n                                                      var10004 = var10003;\n                                                      var6 = var10005;\n                                                      if (var10005 <= var1) {\n                                                         break label5878;\n                                                      }\n\n                                                      var8 = var10003;\n                                                      var10006 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var10007 = var8[var10006];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10008 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10008 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10008 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10008 = 68;\n                                                            break;\n                                                         default:\n                                                            var10008 = 11;\n                                                      }\n\n                                                      var8[var10006] = (char)(var10007 ^ var10008);\n                                                      ++var1;\n                                                      if (var6 == 0) {\n                                                         var10006 = var6;\n                                                         var8 = var10004;\n                                                      } else {\n                                                         if (var6 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var8 = var10004;\n                                                         var10006 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                var10000[7] = (new String(var10004)).intern();\n                                                var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                                var10005 = var10003.length;\n                                                var1 = 0;\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= 1) {\n                                                   var8 = var10003;\n                                                   var10006 = var1;\n                                                   var10007 = var10003[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   var10004 = var10003;\n                                                   var6 = var10005;\n                                                   if (var10005 <= var1) {\n                                                      label5946: {\n                                                         var10000[8] = (new String(var10003)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label5946;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10003;\n                                                   var10006 = var1;\n                                                   var10007 = var10003[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n\n                                                while(true) {\n                                                   while(true) {\n                                                      var8[var10006] = (char)(var10007 ^ var10008);\n                                                      ++var1;\n                                                      if (var6 == 0) {\n                                                         var10006 = var6;\n                                                         var8 = var10004;\n                                                         var10007 = var10004[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      } else {\n                                                         if (var6 <= var1) {\n                                                            label6054: {\n                                                               var10000[8] = (new String(var10004)).intern();\n                                                               h = var10000;\n                                                               var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                               var10002 = var2.length;\n                                                               var1 = 0;\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= 1) {\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               } else {\n                                                                  var10001 = var2;\n                                                                  var3 = var10002;\n                                                                  if (var10002 <= var1) {\n                                                                     break label6054;\n                                                                  }\n\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               }\n\n                                                               while(true) {\n                                                                  var9 = var5[var6];\n                                                                  switch (var1 % 5) {\n                                                                     case 0:\n                                                                        var10 = 71;\n                                                                        break;\n                                                                     case 1:\n                                                                        var10 = 81;\n                                                                        break;\n                                                                     case 2:\n                                                                        var10 = 80;\n                                                                        break;\n                                                                     case 3:\n                                                                        var10 = 68;\n                                                                        break;\n                                                                     default:\n                                                                        var10 = 11;\n                                                                  }\n\n                                                                  var5[var6] = (char)(var9 ^ var10);\n                                                                  ++var1;\n                                                                  if (var3 == 0) {\n                                                                     var6 = var3;\n                                                                     var5 = var10001;\n                                                                  } else {\n                                                                     if (var3 <= var1) {\n                                                                        break;\n                                                                     }\n\n                                                                     var5 = var10001;\n                                                                     var6 = var1;\n                                                                  }\n                                                               }\n                                                            }\n\n                                                            a = Pattern.compile((new String(var10001)).intern());\n                                                            var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                            var10002 = var2.length;\n                                                            var1 = 0;\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= 1) {\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            } else {\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= var1) {\n                                                                  g = Pattern.compile((new String(var2)).intern());\n                                                                  return;\n                                                               }\n\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            }\n\n                                                            while(true) {\n                                                               var9 = var5[var6];\n                                                               switch (var1 % 5) {\n                                                                  case 0:\n                                                                     var10 = 71;\n                                                                     break;\n                                                                  case 1:\n                                                                     var10 = 81;\n                                                                     break;\n                                                                  case 2:\n                                                                     var10 = 80;\n                                                                     break;\n                                                                  case 3:\n                                                                     var10 = 68;\n                                                                     break;\n                                                                  default:\n                                                                     var10 = 11;\n                                                               }\n\n                                                               var5[var6] = (char)(var9 ^ var10);\n                                                               ++var1;\n                                                               if (var3 == 0) {\n                                                                  var6 = var3;\n                                                                  var5 = var10001;\n                                                               } else {\n                                                                  if (var3 <= var1) {\n                                                                     g = Pattern.compile((new String(var10001)).intern());\n                                                                     return;\n                                                                  }\n\n                                                                  var5 = var10001;\n                                                                  var6 = var1;\n                                                               }\n                                                            }\n                                                         }\n\n                                                         var8 = var10004;\n                                                         var10006 = var1;\n                                                         var10007 = var10004[var1];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      }\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                             var10007 = var10004[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                                 var10007 = var10004[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              }\n                           }\n                        }\n                     }\n\n                     var8 = var10004;\n                     var10006 = var1;\n                     var10007 = var10004[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 71;\n                           break;\n                        case 1:\n                           var10008 = 81;\n                           break;\n                        case 2:\n                           var10008 = 80;\n                           break;\n                        case 3:\n                           var10008 = 68;\n                           break;\n                        default:\n                           var10008 = 11;\n                     }\n                  }\n               }\n            }\n         }\n\n         var8 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 71;\n               break;\n            case 1:\n               var10008 = 81;\n               break;\n            case 2:\n               var10008 = 80;\n               break;\n            case 3:\n               var10008 = 68;\n               break;\n            default:\n               var10008 = 11;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var8[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var6 == 0) {\n               var10006 = var6;\n               var8 = var10004;\n               var10007 = var10004[var6];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 71;\n                     break;\n                  case 1:\n                     var10008 = 81;\n                     break;\n                  case 2:\n                     var10008 = 80;\n                     break;\n                  case 3:\n                     var10008 = 68;\n                     break;\n                  default:\n                     var10008 = 11;\n               }\n            } else {\n               if (var6 <= var1) {\n                  label1509: {\n                     var10000[0] = (new String(var10004)).intern();\n                     var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var6 = var10005;\n                     if (var10005 <= 1) {\n                        var8 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= var1) {\n                           break label1509;\n                        }\n\n                        var8 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var8[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 71;\n                              break;\n                           case 1:\n                              var10008 = 81;\n                              break;\n                           case 2:\n                              var10008 = 80;\n                              break;\n                           case 3:\n                              var10008 = 68;\n                              break;\n                           default:\n                              var10008 = 11;\n                        }\n\n                        var8[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var6 == 0) {\n                           var10006 = var6;\n                           var8 = var10004;\n                        } else {\n                           if (var6 <= var1) {\n                              break;\n                           }\n\n                           var8 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[1] = (new String(var10004)).intern();\n                  var10003 = \"h!\\\"+hh\\\"$%\\u007f\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var6 = var10005;\n                  if (var10005 <= 1) {\n                     var8 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 71;\n                           break;\n                        case 1:\n                           var10008 = 81;\n                           break;\n                        case 2:\n                           var10008 = 80;\n                           break;\n                        case 3:\n                           var10008 = 68;\n                           break;\n                        default:\n                           var10008 = 11;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var6 = var10005;\n                     if (var10005 <= var1) {\n                        label1553: {\n                           var10000[2] = (new String(var10003)).intern();\n                           var10003 = \"j#5%o4\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= 1) {\n                              var8 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= var1) {\n                                 break label1553;\n                              }\n\n                              var8 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var8[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 71;\n                                    break;\n                                 case 1:\n                                    var10008 = 81;\n                                    break;\n                                 case 2:\n                                    var10008 = 80;\n                                    break;\n                                 case 3:\n                                    var10008 = 68;\n                                    break;\n                                 default:\n                                    var10008 = 11;\n                              }\n\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                              } else {\n                                 if (var6 <= var1) {\n                                    break;\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"v~#\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var6 = var10005;\n                        if (var10005 <= 1) {\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        } else {\n                           var10004 = var10003;\n                           var6 = var10005;\n                           if (var10005 <= var1) {\n                              label1621: {\n                                 var10000[4] = (new String(var10003)).intern();\n                                 var10003 = \"j&\\\"-\\u007f\\\"\\\"\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label1621;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 71;\n                                          break;\n                                       case 1:\n                                          var10008 = 81;\n                                          break;\n                                       case 2:\n                                          var10008 = 80;\n                                          break;\n                                       case 3:\n                                          var10008 = 68;\n                                          break;\n                                       default:\n                                          var10008 = 11;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[5] = (new String(var10004)).intern();\n                              var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= 1) {\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              } else {\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= var1) {\n                                    label1689: {\n                                       var10000[6] = (new String(var10003)).intern();\n                                       var10003 = \"v~#\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label1689;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 71;\n                                                break;\n                                             case 1:\n                                                var10008 = 81;\n                                                break;\n                                             case 2:\n                                                var10008 = 80;\n                                                break;\n                                             case 3:\n                                                var10008 = 68;\n                                                break;\n                                             default:\n                                                var10008 = 11;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[7] = (new String(var10004)).intern();\n                                    var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                    var10005 = var10003.length;\n                                    var1 = 0;\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= 1) {\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= var1) {\n                                          label1757: {\n                                             var10000[8] = (new String(var10003)).intern();\n                                             h = var10000;\n                                             var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                             var10002 = var2.length;\n                                             var1 = 0;\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= 1) {\n                                                var5 = var2;\n                                                var6 = var1;\n                                             } else {\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= var1) {\n                                                   break label1757;\n                                                }\n\n                                                var5 = var2;\n                                                var6 = var1;\n                                             }\n\n                                             while(true) {\n                                                var9 = var5[var6];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10 = 68;\n                                                      break;\n                                                   default:\n                                                      var10 = 11;\n                                                }\n\n                                                var5[var6] = (char)(var9 ^ var10);\n                                                ++var1;\n                                                if (var3 == 0) {\n                                                   var6 = var3;\n                                                   var5 = var10001;\n                                                } else {\n                                                   if (var3 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var5 = var10001;\n                                                   var6 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          a = Pattern.compile((new String(var10001)).intern());\n                                          var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                          var10002 = var2.length;\n                                          var1 = 0;\n                                          var10001 = var2;\n                                          var3 = var10002;\n                                          if (var10002 <= 1) {\n                                             var5 = var2;\n                                             var6 = var1;\n                                          } else {\n                                             var10001 = var2;\n                                             var3 = var10002;\n                                             if (var10002 <= var1) {\n                                                g = Pattern.compile((new String(var2)).intern());\n                                                return;\n                                             }\n\n                                             var5 = var2;\n                                             var6 = var1;\n                                          }\n\n                                          while(true) {\n                                             var9 = var5[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10 = 68;\n                                                   break;\n                                                default:\n                                                   var10 = 11;\n                                             }\n\n                                             var5[var6] = (char)(var9 ^ var10);\n                                             ++var1;\n                                             if (var3 == 0) {\n                                                var6 = var3;\n                                                var5 = var10001;\n                                             } else {\n                                                if (var3 <= var1) {\n                                                   g = Pattern.compile((new String(var10001)).intern());\n                                                   return;\n                                                }\n\n                                                var5 = var10001;\n                                                var6 = var1;\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n\n                                    while(true) {\n                                       while(true) {\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                             var10007 = var10004[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             if (var6 <= var1) {\n                                                label1865: {\n                                                   var10000[8] = (new String(var10004)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label1865;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                             var10007 = var10004[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              }\n\n                              while(true) {\n                                 while(true) {\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                       var10007 = var10004[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       if (var6 <= var1) {\n                                          label2000: {\n                                             var10000[6] = (new String(var10004)).intern();\n                                             var10003 = \"v~#\".toCharArray();\n                                             var10005 = var10003.length;\n                                             var1 = 0;\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= 1) {\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             } else {\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= var1) {\n                                                   break label2000;\n                                                }\n\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             }\n\n                                             while(true) {\n                                                var10007 = var8[var10006];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10008 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10008 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10008 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10008 = 68;\n                                                      break;\n                                                   default:\n                                                      var10008 = 11;\n                                                }\n\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          var10000[7] = (new String(var10004)).intern();\n                                          var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                          var10005 = var10003.length;\n                                          var1 = 0;\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= 1) {\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= var1) {\n                                                label2068: {\n                                                   var10000[8] = (new String(var10003)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label2068;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n\n                                          while(true) {\n                                             while(true) {\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                   var10007 = var10004[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      label2176: {\n                                                         var10000[8] = (new String(var10004)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label2176;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                   var10007 = var10004[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                       var10007 = var10004[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n                                 }\n                              }\n                           }\n\n                           var8 = var10003;\n                           var10006 = var1;\n                           var10007 = var10003[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        }\n\n                        while(true) {\n                           while(true) {\n                              var8[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var6 == 0) {\n                                 var10006 = var6;\n                                 var8 = var10004;\n                                 var10007 = var10004[var6];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              } else {\n                                 if (var6 <= var1) {\n                                    label2338: {\n                                       var10000[4] = (new String(var10004)).intern();\n                                       var10003 = \"j&\\\"-\\u007f\\\"\\\"\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label2338;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 71;\n                                                break;\n                                             case 1:\n                                                var10008 = 81;\n                                                break;\n                                             case 2:\n                                                var10008 = 80;\n                                                break;\n                                             case 3:\n                                                var10008 = 68;\n                                                break;\n                                             default:\n                                                var10008 = 11;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[5] = (new String(var10004)).intern();\n                                    var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                    var10005 = var10003.length;\n                                    var1 = 0;\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= 1) {\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= var1) {\n                                          label2406: {\n                                             var10000[6] = (new String(var10003)).intern();\n                                             var10003 = \"v~#\".toCharArray();\n                                             var10005 = var10003.length;\n                                             var1 = 0;\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= 1) {\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             } else {\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= var1) {\n                                                   break label2406;\n                                                }\n\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             }\n\n                                             while(true) {\n                                                var10007 = var8[var10006];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10008 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10008 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10008 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10008 = 68;\n                                                      break;\n                                                   default:\n                                                      var10008 = 11;\n                                                }\n\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          var10000[7] = (new String(var10004)).intern();\n                                          var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                          var10005 = var10003.length;\n                                          var1 = 0;\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= 1) {\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= var1) {\n                                                label2474: {\n                                                   var10000[8] = (new String(var10003)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label2474;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n\n                                          while(true) {\n                                             while(true) {\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                   var10007 = var10004[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      label2582: {\n                                                         var10000[8] = (new String(var10004)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label2582;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                   var10007 = var10004[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n\n                                    while(true) {\n                                       while(true) {\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                             var10007 = var10004[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             if (var6 <= var1) {\n                                                label2717: {\n                                                   var10000[6] = (new String(var10004)).intern();\n                                                   var10003 = \"v~#\".toCharArray();\n                                                   var10005 = var10003.length;\n                                                   var1 = 0;\n                                                   var10004 = var10003;\n                                                   var6 = var10005;\n                                                   if (var10005 <= 1) {\n                                                      var8 = var10003;\n                                                      var10006 = var1;\n                                                   } else {\n                                                      var10004 = var10003;\n                                                      var6 = var10005;\n                                                      if (var10005 <= var1) {\n                                                         break label2717;\n                                                      }\n\n                                                      var8 = var10003;\n                                                      var10006 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var10007 = var8[var10006];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10008 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10008 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10008 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10008 = 68;\n                                                            break;\n                                                         default:\n                                                            var10008 = 11;\n                                                      }\n\n                                                      var8[var10006] = (char)(var10007 ^ var10008);\n                                                      ++var1;\n                                                      if (var6 == 0) {\n                                                         var10006 = var6;\n                                                         var8 = var10004;\n                                                      } else {\n                                                         if (var6 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var8 = var10004;\n                                                         var10006 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                var10000[7] = (new String(var10004)).intern();\n                                                var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                                var10005 = var10003.length;\n                                                var1 = 0;\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= 1) {\n                                                   var8 = var10003;\n                                                   var10006 = var1;\n                                                   var10007 = var10003[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   var10004 = var10003;\n                                                   var6 = var10005;\n                                                   if (var10005 <= var1) {\n                                                      label2785: {\n                                                         var10000[8] = (new String(var10003)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label2785;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10003;\n                                                   var10006 = var1;\n                                                   var10007 = var10003[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n\n                                                while(true) {\n                                                   while(true) {\n                                                      var8[var10006] = (char)(var10007 ^ var10008);\n                                                      ++var1;\n                                                      if (var6 == 0) {\n                                                         var10006 = var6;\n                                                         var8 = var10004;\n                                                         var10007 = var10004[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      } else {\n                                                         if (var6 <= var1) {\n                                                            label2893: {\n                                                               var10000[8] = (new String(var10004)).intern();\n                                                               h = var10000;\n                                                               var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                               var10002 = var2.length;\n                                                               var1 = 0;\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= 1) {\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               } else {\n                                                                  var10001 = var2;\n                                                                  var3 = var10002;\n                                                                  if (var10002 <= var1) {\n                                                                     break label2893;\n                                                                  }\n\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               }\n\n                                                               while(true) {\n                                                                  var9 = var5[var6];\n                                                                  switch (var1 % 5) {\n                                                                     case 0:\n                                                                        var10 = 71;\n                                                                        break;\n                                                                     case 1:\n                                                                        var10 = 81;\n                                                                        break;\n                                                                     case 2:\n                                                                        var10 = 80;\n                                                                        break;\n                                                                     case 3:\n                                                                        var10 = 68;\n                                                                        break;\n                                                                     default:\n                                                                        var10 = 11;\n                                                                  }\n\n                                                                  var5[var6] = (char)(var9 ^ var10);\n                                                                  ++var1;\n                                                                  if (var3 == 0) {\n                                                                     var6 = var3;\n                                                                     var5 = var10001;\n                                                                  } else {\n                                                                     if (var3 <= var1) {\n                                                                        break;\n                                                                     }\n\n                                                                     var5 = var10001;\n                                                                     var6 = var1;\n                                                                  }\n                                                               }\n                                                            }\n\n                                                            a = Pattern.compile((new String(var10001)).intern());\n                                                            var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                            var10002 = var2.length;\n                                                            var1 = 0;\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= 1) {\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            } else {\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= var1) {\n                                                                  g = Pattern.compile((new String(var2)).intern());\n                                                                  return;\n                                                               }\n\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            }\n\n                                                            while(true) {\n                                                               var9 = var5[var6];\n                                                               switch (var1 % 5) {\n                                                                  case 0:\n                                                                     var10 = 71;\n                                                                     break;\n                                                                  case 1:\n                                                                     var10 = 81;\n                                                                     break;\n                                                                  case 2:\n                                                                     var10 = 80;\n                                                                     break;\n                                                                  case 3:\n                                                                     var10 = 68;\n                                                                     break;\n                                                                  default:\n                                                                     var10 = 11;\n                                                               }\n\n                                                               var5[var6] = (char)(var9 ^ var10);\n                                                               ++var1;\n                                                               if (var3 == 0) {\n                                                                  var6 = var3;\n                                                                  var5 = var10001;\n                                                               } else {\n                                                                  if (var3 <= var1) {\n                                                                     g = Pattern.compile((new String(var10001)).intern());\n                                                                     return;\n                                                                  }\n\n                                                                  var5 = var10001;\n                                                                  var6 = var1;\n                                                               }\n                                                            }\n                                                         }\n\n                                                         var8 = var10004;\n                                                         var10006 = var1;\n                                                         var10007 = var10004[var1];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      }\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                             var10007 = var10004[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10004;\n                                 var10006 = var1;\n                                 var10007 = var10004[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              }\n                           }\n                        }\n                     }\n\n                     var8 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 71;\n                           break;\n                        case 1:\n                           var10008 = 81;\n                           break;\n                        case 2:\n                           var10008 = 80;\n                           break;\n                        case 3:\n                           var10008 = 68;\n                           break;\n                        default:\n                           var10008 = 11;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var8[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var6 == 0) {\n                           var10006 = var6;\n                           var8 = var10004;\n                           var10007 = var10004[var6];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        } else {\n                           if (var6 <= var1) {\n                              label739: {\n                                 var10000[2] = (new String(var10004)).intern();\n                                 var10003 = \"j#5%o4\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= 1) {\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label739;\n                                    }\n\n                                    var8 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var8[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 71;\n                                          break;\n                                       case 1:\n                                          var10008 = 81;\n                                          break;\n                                       case 2:\n                                          var10008 = 80;\n                                          break;\n                                       case 3:\n                                          var10008 = 68;\n                                          break;\n                                       default:\n                                          var10008 = 11;\n                                    }\n\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                    } else {\n                                       if (var6 <= var1) {\n                                          break;\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[3] = (new String(var10004)).intern();\n                              var10003 = \"v~#\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var6 = var10005;\n                              if (var10005 <= 1) {\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              } else {\n                                 var10004 = var10003;\n                                 var6 = var10005;\n                                 if (var10005 <= var1) {\n                                    label783: {\n                                       var10000[4] = (new String(var10003)).intern();\n                                       var10003 = \"j&\\\"-\\u007f\\\"\\\"\".toCharArray();\n                                       var10005 = var10003.length;\n                                       var1 = 0;\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= 1) {\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       } else {\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= var1) {\n                                             break label783;\n                                          }\n\n                                          var8 = var10003;\n                                          var10006 = var1;\n                                       }\n\n                                       while(true) {\n                                          var10007 = var8[var10006];\n                                          switch (var1 % 5) {\n                                             case 0:\n                                                var10008 = 71;\n                                                break;\n                                             case 1:\n                                                var10008 = 81;\n                                                break;\n                                             case 2:\n                                                var10008 = 80;\n                                                break;\n                                             case 3:\n                                                var10008 = 68;\n                                                break;\n                                             default:\n                                                var10008 = 11;\n                                          }\n\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                          } else {\n                                             if (var6 <= var1) {\n                                                break;\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                          }\n                                       }\n                                    }\n\n                                    var10000[5] = (new String(var10004)).intern();\n                                    var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                    var10005 = var10003.length;\n                                    var1 = 0;\n                                    var10004 = var10003;\n                                    var6 = var10005;\n                                    if (var10005 <= 1) {\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       var10004 = var10003;\n                                       var6 = var10005;\n                                       if (var10005 <= var1) {\n                                          label851: {\n                                             var10000[6] = (new String(var10003)).intern();\n                                             var10003 = \"v~#\".toCharArray();\n                                             var10005 = var10003.length;\n                                             var1 = 0;\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= 1) {\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             } else {\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= var1) {\n                                                   break label851;\n                                                }\n\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             }\n\n                                             while(true) {\n                                                var10007 = var8[var10006];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10008 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10008 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10008 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10008 = 68;\n                                                      break;\n                                                   default:\n                                                      var10008 = 11;\n                                                }\n\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          var10000[7] = (new String(var10004)).intern();\n                                          var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                          var10005 = var10003.length;\n                                          var1 = 0;\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= 1) {\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= var1) {\n                                                label919: {\n                                                   var10000[8] = (new String(var10003)).intern();\n                                                   h = var10000;\n                                                   var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                   var10002 = var2.length;\n                                                   var1 = 0;\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= 1) {\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   } else {\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= var1) {\n                                                         break label919;\n                                                      }\n\n                                                      var5 = var2;\n                                                      var6 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var9 = var5[var6];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10 = 68;\n                                                            break;\n                                                         default:\n                                                            var10 = 11;\n                                                      }\n\n                                                      var5[var6] = (char)(var9 ^ var10);\n                                                      ++var1;\n                                                      if (var3 == 0) {\n                                                         var6 = var3;\n                                                         var5 = var10001;\n                                                      } else {\n                                                         if (var3 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var5 = var10001;\n                                                         var6 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                a = Pattern.compile((new String(var10001)).intern());\n                                                var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                var10002 = var2.length;\n                                                var1 = 0;\n                                                var10001 = var2;\n                                                var3 = var10002;\n                                                if (var10002 <= 1) {\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                } else {\n                                                   var10001 = var2;\n                                                   var3 = var10002;\n                                                   if (var10002 <= var1) {\n                                                      g = Pattern.compile((new String(var2)).intern());\n                                                      return;\n                                                   }\n\n                                                   var5 = var2;\n                                                   var6 = var1;\n                                                }\n\n                                                while(true) {\n                                                   var9 = var5[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10 = 68;\n                                                         break;\n                                                      default:\n                                                         var10 = 11;\n                                                   }\n\n                                                   var5[var6] = (char)(var9 ^ var10);\n                                                   ++var1;\n                                                   if (var3 == 0) {\n                                                      var6 = var3;\n                                                      var5 = var10001;\n                                                   } else {\n                                                      if (var3 <= var1) {\n                                                         g = Pattern.compile((new String(var10001)).intern());\n                                                         return;\n                                                      }\n\n                                                      var5 = var10001;\n                                                      var6 = var1;\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n\n                                          while(true) {\n                                             while(true) {\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                   var10007 = var10004[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      label1027: {\n                                                         var10000[8] = (new String(var10004)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label1027;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                   var10007 = var10004[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10003;\n                                       var10006 = var1;\n                                       var10007 = var10003[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n\n                                    while(true) {\n                                       while(true) {\n                                          var8[var10006] = (char)(var10007 ^ var10008);\n                                          ++var1;\n                                          if (var6 == 0) {\n                                             var10006 = var6;\n                                             var8 = var10004;\n                                             var10007 = var10004[var6];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             if (var6 <= var1) {\n                                                label1162: {\n                                                   var10000[6] = (new String(var10004)).intern();\n                                                   var10003 = \"v~#\".toCharArray();\n                                                   var10005 = var10003.length;\n                                                   var1 = 0;\n                                                   var10004 = var10003;\n                                                   var6 = var10005;\n                                                   if (var10005 <= 1) {\n                                                      var8 = var10003;\n                                                      var10006 = var1;\n                                                   } else {\n                                                      var10004 = var10003;\n                                                      var6 = var10005;\n                                                      if (var10005 <= var1) {\n                                                         break label1162;\n                                                      }\n\n                                                      var8 = var10003;\n                                                      var10006 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var10007 = var8[var10006];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10008 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10008 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10008 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10008 = 68;\n                                                            break;\n                                                         default:\n                                                            var10008 = 11;\n                                                      }\n\n                                                      var8[var10006] = (char)(var10007 ^ var10008);\n                                                      ++var1;\n                                                      if (var6 == 0) {\n                                                         var10006 = var6;\n                                                         var8 = var10004;\n                                                      } else {\n                                                         if (var6 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var8 = var10004;\n                                                         var10006 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                var10000[7] = (new String(var10004)).intern();\n                                                var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                                var10005 = var10003.length;\n                                                var1 = 0;\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= 1) {\n                                                   var8 = var10003;\n                                                   var10006 = var1;\n                                                   var10007 = var10003[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   var10004 = var10003;\n                                                   var6 = var10005;\n                                                   if (var10005 <= var1) {\n                                                      label1230: {\n                                                         var10000[8] = (new String(var10003)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label1230;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10003;\n                                                   var10006 = var1;\n                                                   var10007 = var10003[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n\n                                                while(true) {\n                                                   while(true) {\n                                                      var8[var10006] = (char)(var10007 ^ var10008);\n                                                      ++var1;\n                                                      if (var6 == 0) {\n                                                         var10006 = var6;\n                                                         var8 = var10004;\n                                                         var10007 = var10004[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      } else {\n                                                         if (var6 <= var1) {\n                                                            label1338: {\n                                                               var10000[8] = (new String(var10004)).intern();\n                                                               h = var10000;\n                                                               var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                               var10002 = var2.length;\n                                                               var1 = 0;\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= 1) {\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               } else {\n                                                                  var10001 = var2;\n                                                                  var3 = var10002;\n                                                                  if (var10002 <= var1) {\n                                                                     break label1338;\n                                                                  }\n\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               }\n\n                                                               while(true) {\n                                                                  var9 = var5[var6];\n                                                                  switch (var1 % 5) {\n                                                                     case 0:\n                                                                        var10 = 71;\n                                                                        break;\n                                                                     case 1:\n                                                                        var10 = 81;\n                                                                        break;\n                                                                     case 2:\n                                                                        var10 = 80;\n                                                                        break;\n                                                                     case 3:\n                                                                        var10 = 68;\n                                                                        break;\n                                                                     default:\n                                                                        var10 = 11;\n                                                                  }\n\n                                                                  var5[var6] = (char)(var9 ^ var10);\n                                                                  ++var1;\n                                                                  if (var3 == 0) {\n                                                                     var6 = var3;\n                                                                     var5 = var10001;\n                                                                  } else {\n                                                                     if (var3 <= var1) {\n                                                                        break;\n                                                                     }\n\n                                                                     var5 = var10001;\n                                                                     var6 = var1;\n                                                                  }\n                                                               }\n                                                            }\n\n                                                            a = Pattern.compile((new String(var10001)).intern());\n                                                            var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                            var10002 = var2.length;\n                                                            var1 = 0;\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= 1) {\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            } else {\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= var1) {\n                                                                  g = Pattern.compile((new String(var2)).intern());\n                                                                  return;\n                                                               }\n\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            }\n\n                                                            while(true) {\n                                                               var9 = var5[var6];\n                                                               switch (var1 % 5) {\n                                                                  case 0:\n                                                                     var10 = 71;\n                                                                     break;\n                                                                  case 1:\n                                                                     var10 = 81;\n                                                                     break;\n                                                                  case 2:\n                                                                     var10 = 80;\n                                                                     break;\n                                                                  case 3:\n                                                                     var10 = 68;\n                                                                     break;\n                                                                  default:\n                                                                     var10 = 11;\n                                                               }\n\n                                                               var5[var6] = (char)(var9 ^ var10);\n                                                               ++var1;\n                                                               if (var3 == 0) {\n                                                                  var6 = var3;\n                                                                  var5 = var10001;\n                                                               } else {\n                                                                  if (var3 <= var1) {\n                                                                     g = Pattern.compile((new String(var10001)).intern());\n                                                                     return;\n                                                                  }\n\n                                                                  var5 = var10001;\n                                                                  var6 = var1;\n                                                               }\n                                                            }\n                                                         }\n\n                                                         var8 = var10004;\n                                                         var10006 = var1;\n                                                         var10007 = var10004[var1];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      }\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10004;\n                                             var10006 = var1;\n                                             var10007 = var10004[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n                                       }\n                                    }\n                                 }\n\n                                 var8 = var10003;\n                                 var10006 = var1;\n                                 var10007 = var10003[var1];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 71;\n                                       break;\n                                    case 1:\n                                       var10008 = 81;\n                                       break;\n                                    case 2:\n                                       var10008 = 80;\n                                       break;\n                                    case 3:\n                                       var10008 = 68;\n                                       break;\n                                    default:\n                                       var10008 = 11;\n                                 }\n                              }\n\n                              while(true) {\n                                 while(true) {\n                                    var8[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var6 == 0) {\n                                       var10006 = var6;\n                                       var8 = var10004;\n                                       var10007 = var10004[var6];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    } else {\n                                       if (var6 <= var1) {\n                                          label375: {\n                                             var10000[4] = (new String(var10004)).intern();\n                                             var10003 = \"j&\\\"-\\u007f\\\"\\\"\".toCharArray();\n                                             var10005 = var10003.length;\n                                             var1 = 0;\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= 1) {\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             } else {\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= var1) {\n                                                   break label375;\n                                                }\n\n                                                var8 = var10003;\n                                                var10006 = var1;\n                                             }\n\n                                             while(true) {\n                                                var10007 = var8[var10006];\n                                                switch (var1 % 5) {\n                                                   case 0:\n                                                      var10008 = 71;\n                                                      break;\n                                                   case 1:\n                                                      var10008 = 81;\n                                                      break;\n                                                   case 2:\n                                                      var10008 = 80;\n                                                      break;\n                                                   case 3:\n                                                      var10008 = 68;\n                                                      break;\n                                                   default:\n                                                      var10008 = 11;\n                                                }\n\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      break;\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                }\n                                             }\n                                          }\n\n                                          var10000[5] = (new String(var10004)).intern();\n                                          var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                          var10005 = var10003.length;\n                                          var1 = 0;\n                                          var10004 = var10003;\n                                          var6 = var10005;\n                                          if (var10005 <= 1) {\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          } else {\n                                             var10004 = var10003;\n                                             var6 = var10005;\n                                             if (var10005 <= var1) {\n                                                label419: {\n                                                   var10000[6] = (new String(var10003)).intern();\n                                                   var10003 = \"v~#\".toCharArray();\n                                                   var10005 = var10003.length;\n                                                   var1 = 0;\n                                                   var10004 = var10003;\n                                                   var6 = var10005;\n                                                   if (var10005 <= 1) {\n                                                      var8 = var10003;\n                                                      var10006 = var1;\n                                                   } else {\n                                                      var10004 = var10003;\n                                                      var6 = var10005;\n                                                      if (var10005 <= var1) {\n                                                         break label419;\n                                                      }\n\n                                                      var8 = var10003;\n                                                      var10006 = var1;\n                                                   }\n\n                                                   while(true) {\n                                                      var10007 = var8[var10006];\n                                                      switch (var1 % 5) {\n                                                         case 0:\n                                                            var10008 = 71;\n                                                            break;\n                                                         case 1:\n                                                            var10008 = 81;\n                                                            break;\n                                                         case 2:\n                                                            var10008 = 80;\n                                                            break;\n                                                         case 3:\n                                                            var10008 = 68;\n                                                            break;\n                                                         default:\n                                                            var10008 = 11;\n                                                      }\n\n                                                      var8[var10006] = (char)(var10007 ^ var10008);\n                                                      ++var1;\n                                                      if (var6 == 0) {\n                                                         var10006 = var6;\n                                                         var8 = var10004;\n                                                      } else {\n                                                         if (var6 <= var1) {\n                                                            break;\n                                                         }\n\n                                                         var8 = var10004;\n                                                         var10006 = var1;\n                                                      }\n                                                   }\n                                                }\n\n                                                var10000[7] = (new String(var10004)).intern();\n                                                var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                                var10005 = var10003.length;\n                                                var1 = 0;\n                                                var10004 = var10003;\n                                                var6 = var10005;\n                                                if (var10005 <= 1) {\n                                                   var8 = var10003;\n                                                   var10006 = var1;\n                                                   var10007 = var10003[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   var10004 = var10003;\n                                                   var6 = var10005;\n                                                   if (var10005 <= var1) {\n                                                      label487: {\n                                                         var10000[8] = (new String(var10003)).intern();\n                                                         h = var10000;\n                                                         var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                         var10002 = var2.length;\n                                                         var1 = 0;\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= 1) {\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         } else {\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= var1) {\n                                                               break label487;\n                                                            }\n\n                                                            var5 = var2;\n                                                            var6 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var9 = var5[var6];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10 = 11;\n                                                            }\n\n                                                            var5[var6] = (char)(var9 ^ var10);\n                                                            ++var1;\n                                                            if (var3 == 0) {\n                                                               var6 = var3;\n                                                               var5 = var10001;\n                                                            } else {\n                                                               if (var3 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var5 = var10001;\n                                                               var6 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      a = Pattern.compile((new String(var10001)).intern());\n                                                      var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                      var10002 = var2.length;\n                                                      var1 = 0;\n                                                      var10001 = var2;\n                                                      var3 = var10002;\n                                                      if (var10002 <= 1) {\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      } else {\n                                                         var10001 = var2;\n                                                         var3 = var10002;\n                                                         if (var10002 <= var1) {\n                                                            g = Pattern.compile((new String(var2)).intern());\n                                                            return;\n                                                         }\n\n                                                         var5 = var2;\n                                                         var6 = var1;\n                                                      }\n\n                                                      while(true) {\n                                                         var9 = var5[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10 = 68;\n                                                               break;\n                                                            default:\n                                                               var10 = 11;\n                                                         }\n\n                                                         var5[var6] = (char)(var9 ^ var10);\n                                                         ++var1;\n                                                         if (var3 == 0) {\n                                                            var6 = var3;\n                                                            var5 = var10001;\n                                                         } else {\n                                                            if (var3 <= var1) {\n                                                               g = Pattern.compile((new String(var10001)).intern());\n                                                               return;\n                                                            }\n\n                                                            var5 = var10001;\n                                                            var6 = var1;\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10003;\n                                                   var10006 = var1;\n                                                   var10007 = var10003[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n\n                                                while(true) {\n                                                   while(true) {\n                                                      var8[var10006] = (char)(var10007 ^ var10008);\n                                                      ++var1;\n                                                      if (var6 == 0) {\n                                                         var10006 = var6;\n                                                         var8 = var10004;\n                                                         var10007 = var10004[var6];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      } else {\n                                                         if (var6 <= var1) {\n                                                            label595: {\n                                                               var10000[8] = (new String(var10004)).intern();\n                                                               h = var10000;\n                                                               var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                               var10002 = var2.length;\n                                                               var1 = 0;\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= 1) {\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               } else {\n                                                                  var10001 = var2;\n                                                                  var3 = var10002;\n                                                                  if (var10002 <= var1) {\n                                                                     break label595;\n                                                                  }\n\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               }\n\n                                                               while(true) {\n                                                                  var9 = var5[var6];\n                                                                  switch (var1 % 5) {\n                                                                     case 0:\n                                                                        var10 = 71;\n                                                                        break;\n                                                                     case 1:\n                                                                        var10 = 81;\n                                                                        break;\n                                                                     case 2:\n                                                                        var10 = 80;\n                                                                        break;\n                                                                     case 3:\n                                                                        var10 = 68;\n                                                                        break;\n                                                                     default:\n                                                                        var10 = 11;\n                                                                  }\n\n                                                                  var5[var6] = (char)(var9 ^ var10);\n                                                                  ++var1;\n                                                                  if (var3 == 0) {\n                                                                     var6 = var3;\n                                                                     var5 = var10001;\n                                                                  } else {\n                                                                     if (var3 <= var1) {\n                                                                        break;\n                                                                     }\n\n                                                                     var5 = var10001;\n                                                                     var6 = var1;\n                                                                  }\n                                                               }\n                                                            }\n\n                                                            a = Pattern.compile((new String(var10001)).intern());\n                                                            var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                            var10002 = var2.length;\n                                                            var1 = 0;\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= 1) {\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            } else {\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= var1) {\n                                                                  g = Pattern.compile((new String(var2)).intern());\n                                                                  return;\n                                                               }\n\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            }\n\n                                                            while(true) {\n                                                               var9 = var5[var6];\n                                                               switch (var1 % 5) {\n                                                                  case 0:\n                                                                     var10 = 71;\n                                                                     break;\n                                                                  case 1:\n                                                                     var10 = 81;\n                                                                     break;\n                                                                  case 2:\n                                                                     var10 = 80;\n                                                                     break;\n                                                                  case 3:\n                                                                     var10 = 68;\n                                                                     break;\n                                                                  default:\n                                                                     var10 = 11;\n                                                               }\n\n                                                               var5[var6] = (char)(var9 ^ var10);\n                                                               ++var1;\n                                                               if (var3 == 0) {\n                                                                  var6 = var3;\n                                                                  var5 = var10001;\n                                                               } else {\n                                                                  if (var3 <= var1) {\n                                                                     g = Pattern.compile((new String(var10001)).intern());\n                                                                     return;\n                                                                  }\n\n                                                                  var5 = var10001;\n                                                                  var6 = var1;\n                                                               }\n                                                            }\n                                                         }\n\n                                                         var8 = var10004;\n                                                         var10006 = var1;\n                                                         var10007 = var10004[var1];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      }\n                                                   }\n                                                }\n                                             }\n\n                                             var8 = var10003;\n                                             var10006 = var1;\n                                             var10007 = var10003[var1];\n                                             switch (var1 % 5) {\n                                                case 0:\n                                                   var10008 = 71;\n                                                   break;\n                                                case 1:\n                                                   var10008 = 81;\n                                                   break;\n                                                case 2:\n                                                   var10008 = 80;\n                                                   break;\n                                                case 3:\n                                                   var10008 = 68;\n                                                   break;\n                                                default:\n                                                   var10008 = 11;\n                                             }\n                                          }\n\n                                          while(true) {\n                                             while(true) {\n                                                var8[var10006] = (char)(var10007 ^ var10008);\n                                                ++var1;\n                                                if (var6 == 0) {\n                                                   var10006 = var6;\n                                                   var8 = var10004;\n                                                   var10007 = var10004[var6];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                } else {\n                                                   if (var6 <= var1) {\n                                                      label214: {\n                                                         var10000[6] = (new String(var10004)).intern();\n                                                         var10003 = \"v~#\".toCharArray();\n                                                         var10005 = var10003.length;\n                                                         var1 = 0;\n                                                         var10004 = var10003;\n                                                         var6 = var10005;\n                                                         if (var10005 <= 1) {\n                                                            var8 = var10003;\n                                                            var10006 = var1;\n                                                         } else {\n                                                            var10004 = var10003;\n                                                            var6 = var10005;\n                                                            if (var10005 <= var1) {\n                                                               break label214;\n                                                            }\n\n                                                            var8 = var10003;\n                                                            var10006 = var1;\n                                                         }\n\n                                                         while(true) {\n                                                            var10007 = var8[var10006];\n                                                            switch (var1 % 5) {\n                                                               case 0:\n                                                                  var10008 = 71;\n                                                                  break;\n                                                               case 1:\n                                                                  var10008 = 81;\n                                                                  break;\n                                                               case 2:\n                                                                  var10008 = 80;\n                                                                  break;\n                                                               case 3:\n                                                                  var10008 = 68;\n                                                                  break;\n                                                               default:\n                                                                  var10008 = 11;\n                                                            }\n\n                                                            var8[var10006] = (char)(var10007 ^ var10008);\n                                                            ++var1;\n                                                            if (var6 == 0) {\n                                                               var10006 = var6;\n                                                               var8 = var10004;\n                                                            } else {\n                                                               if (var6 <= var1) {\n                                                                  break;\n                                                               }\n\n                                                               var8 = var10004;\n                                                               var10006 = var1;\n                                                            }\n                                                         }\n                                                      }\n\n                                                      var10000[7] = (new String(var10004)).intern();\n                                                      var10003 = \"\\u0014\\b\\u0003\".toCharArray();\n                                                      var10005 = var10003.length;\n                                                      var1 = 0;\n                                                      var10004 = var10003;\n                                                      var6 = var10005;\n                                                      if (var10005 <= 1) {\n                                                         var8 = var10003;\n                                                         var10006 = var1;\n                                                         var10007 = var10003[var1];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      } else {\n                                                         var10004 = var10003;\n                                                         var6 = var10005;\n                                                         if (var10005 <= var1) {\n                                                            label178: {\n                                                               var10000[8] = (new String(var10003)).intern();\n                                                               h = var10000;\n                                                               var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                               var10002 = var2.length;\n                                                               var1 = 0;\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= 1) {\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               } else {\n                                                                  var10001 = var2;\n                                                                  var3 = var10002;\n                                                                  if (var10002 <= var1) {\n                                                                     break label178;\n                                                                  }\n\n                                                                  var5 = var2;\n                                                                  var6 = var1;\n                                                               }\n\n                                                               while(true) {\n                                                                  var9 = var5[var6];\n                                                                  switch (var1 % 5) {\n                                                                     case 0:\n                                                                        var10 = 71;\n                                                                        break;\n                                                                     case 1:\n                                                                        var10 = 81;\n                                                                        break;\n                                                                     case 2:\n                                                                        var10 = 80;\n                                                                        break;\n                                                                     case 3:\n                                                                        var10 = 68;\n                                                                        break;\n                                                                     default:\n                                                                        var10 = 11;\n                                                                  }\n\n                                                                  var5[var6] = (char)(var9 ^ var10);\n                                                                  ++var1;\n                                                                  if (var3 == 0) {\n                                                                     var6 = var3;\n                                                                     var5 = var10001;\n                                                                  } else {\n                                                                     if (var3 <= var1) {\n                                                                        break;\n                                                                     }\n\n                                                                     var5 = var10001;\n                                                                     var6 = var1;\n                                                                  }\n                                                               }\n                                                            }\n\n                                                            a = Pattern.compile((new String(var10001)).intern());\n                                                            var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                            var10002 = var2.length;\n                                                            var1 = 0;\n                                                            var10001 = var2;\n                                                            var3 = var10002;\n                                                            if (var10002 <= 1) {\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            } else {\n                                                               var10001 = var2;\n                                                               var3 = var10002;\n                                                               if (var10002 <= var1) {\n                                                                  g = Pattern.compile((new String(var2)).intern());\n                                                                  return;\n                                                               }\n\n                                                               var5 = var2;\n                                                               var6 = var1;\n                                                            }\n\n                                                            while(true) {\n                                                               var9 = var5[var6];\n                                                               switch (var1 % 5) {\n                                                                  case 0:\n                                                                     var10 = 71;\n                                                                     break;\n                                                                  case 1:\n                                                                     var10 = 81;\n                                                                     break;\n                                                                  case 2:\n                                                                     var10 = 80;\n                                                                     break;\n                                                                  case 3:\n                                                                     var10 = 68;\n                                                                     break;\n                                                                  default:\n                                                                     var10 = 11;\n                                                               }\n\n                                                               var5[var6] = (char)(var9 ^ var10);\n                                                               ++var1;\n                                                               if (var3 == 0) {\n                                                                  var6 = var3;\n                                                                  var5 = var10001;\n                                                               } else {\n                                                                  if (var3 <= var1) {\n                                                                     g = Pattern.compile((new String(var10001)).intern());\n                                                                     return;\n                                                                  }\n\n                                                                  var5 = var10001;\n                                                                  var6 = var1;\n                                                               }\n                                                            }\n                                                         }\n\n                                                         var8 = var10003;\n                                                         var10006 = var1;\n                                                         var10007 = var10003[var1];\n                                                         switch (var1 % 5) {\n                                                            case 0:\n                                                               var10008 = 71;\n                                                               break;\n                                                            case 1:\n                                                               var10008 = 81;\n                                                               break;\n                                                            case 2:\n                                                               var10008 = 80;\n                                                               break;\n                                                            case 3:\n                                                               var10008 = 68;\n                                                               break;\n                                                            default:\n                                                               var10008 = 11;\n                                                         }\n                                                      }\n\n                                                      while(true) {\n                                                         while(true) {\n                                                            var8[var10006] = (char)(var10007 ^ var10008);\n                                                            ++var1;\n                                                            if (var6 == 0) {\n                                                               var10006 = var6;\n                                                               var8 = var10004;\n                                                               var10007 = var10004[var6];\n                                                               switch (var1 % 5) {\n                                                                  case 0:\n                                                                     var10008 = 71;\n                                                                     break;\n                                                                  case 1:\n                                                                     var10008 = 81;\n                                                                     break;\n                                                                  case 2:\n                                                                     var10008 = 80;\n                                                                     break;\n                                                                  case 3:\n                                                                     var10008 = 68;\n                                                                     break;\n                                                                  default:\n                                                                     var10008 = 11;\n                                                               }\n                                                            } else {\n                                                               if (var6 <= var1) {\n                                                                  label258: {\n                                                                     var10000[8] = (new String(var10004)).intern();\n                                                                     h = var10000;\n                                                                     var2 = \"o2 1W#zyd o\\r4o\\\"iz\".toCharArray();\n                                                                     var10002 = var2.length;\n                                                                     var1 = 0;\n                                                                     var10001 = var2;\n                                                                     var3 = var10002;\n                                                                     if (var10002 <= 1) {\n                                                                        var5 = var2;\n                                                                        var6 = var1;\n                                                                     } else {\n                                                                        var10001 = var2;\n                                                                        var3 = var10002;\n                                                                        if (var10002 <= var1) {\n                                                                           break label258;\n                                                                        }\n\n                                                                        var5 = var2;\n                                                                        var6 = var1;\n                                                                     }\n\n                                                                     while(true) {\n                                                                        var9 = var5[var6];\n                                                                        switch (var1 % 5) {\n                                                                           case 0:\n                                                                              var10 = 71;\n                                                                              break;\n                                                                           case 1:\n                                                                              var10 = 81;\n                                                                              break;\n                                                                           case 2:\n                                                                              var10 = 80;\n                                                                              break;\n                                                                           case 3:\n                                                                              var10 = 68;\n                                                                              break;\n                                                                           default:\n                                                                              var10 = 11;\n                                                                        }\n\n                                                                        var5[var6] = (char)(var9 ^ var10);\n                                                                        ++var1;\n                                                                        if (var3 == 0) {\n                                                                           var6 = var3;\n                                                                           var5 = var10001;\n                                                                        } else {\n                                                                           if (var3 <= var1) {\n                                                                              break;\n                                                                           }\n\n                                                                           var5 = var10001;\n                                                                           var6 = var1;\n                                                                        }\n                                                                     }\n                                                                  }\n\n                                                                  a = Pattern.compile((new String(var10001)).intern());\n                                                                  var2 = \"gz\\f  gz\\f  gzx\\u001fjj+\\ro\\\"gzx\\u0018olxpoW#zpo#\\u001b5{m%m\".toCharArray();\n                                                                  var10002 = var2.length;\n                                                                  var1 = 0;\n                                                                  var10001 = var2;\n                                                                  var3 = var10002;\n                                                                  if (var10002 <= 1) {\n                                                                     var5 = var2;\n                                                                     var6 = var1;\n                                                                  } else {\n                                                                     var10001 = var2;\n                                                                     var3 = var10002;\n                                                                     if (var10002 <= var1) {\n                                                                        g = Pattern.compile((new String(var2)).intern());\n                                                                        return;\n                                                                     }\n\n                                                                     var5 = var2;\n                                                                     var6 = var1;\n                                                                  }\n\n                                                                  while(true) {\n                                                                     var9 = var5[var6];\n                                                                     switch (var1 % 5) {\n                                                                        case 0:\n                                                                           var10 = 71;\n                                                                           break;\n                                                                        case 1:\n                                                                           var10 = 81;\n                                                                           break;\n                                                                        case 2:\n                                                                           var10 = 80;\n                                                                           break;\n                                                                        case 3:\n                                                                           var10 = 68;\n                                                                           break;\n                                                                        default:\n                                                                           var10 = 11;\n                                                                     }\n\n                                                                     var5[var6] = (char)(var9 ^ var10);\n                                                                     ++var1;\n                                                                     if (var3 == 0) {\n                                                                        var6 = var3;\n                                                                        var5 = var10001;\n                                                                     } else {\n                                                                        if (var3 <= var1) {\n                                                                           g = Pattern.compile((new String(var10001)).intern());\n                                                                           return;\n                                                                        }\n\n                                                                        var5 = var10001;\n                                                                        var6 = var1;\n                                                                     }\n                                                                  }\n                                                               }\n\n                                                               var8 = var10004;\n                                                               var10006 = var1;\n                                                               var10007 = var10004[var1];\n                                                               switch (var1 % 5) {\n                                                                  case 0:\n                                                                     var10008 = 71;\n                                                                     break;\n                                                                  case 1:\n                                                                     var10008 = 81;\n                                                                     break;\n                                                                  case 2:\n                                                                     var10008 = 80;\n                                                                     break;\n                                                                  case 3:\n                                                                     var10008 = 68;\n                                                                     break;\n                                                                  default:\n                                                                     var10008 = 11;\n                                                               }\n                                                            }\n                                                         }\n                                                      }\n                                                   }\n\n                                                   var8 = var10004;\n                                                   var10006 = var1;\n                                                   var10007 = var10004[var1];\n                                                   switch (var1 % 5) {\n                                                      case 0:\n                                                         var10008 = 71;\n                                                         break;\n                                                      case 1:\n                                                         var10008 = 81;\n                                                         break;\n                                                      case 2:\n                                                         var10008 = 80;\n                                                         break;\n                                                      case 3:\n                                                         var10008 = 68;\n                                                         break;\n                                                      default:\n                                                         var10008 = 11;\n                                                   }\n                                                }\n                                             }\n                                          }\n                                       }\n\n                                       var8 = var10004;\n                                       var10006 = var1;\n                                       var10007 = var10004[var1];\n                                       switch (var1 % 5) {\n                                          case 0:\n                                             var10008 = 71;\n                                             break;\n                                          case 1:\n                                             var10008 = 81;\n                                             break;\n                                          case 2:\n                                             var10008 = 80;\n                                             break;\n                                          case 3:\n                                             var10008 = 68;\n                                             break;\n                                          default:\n                                             var10008 = 11;\n                                       }\n                                    }\n                                 }\n                              }\n                           }\n\n                           var8 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 71;\n                                 break;\n                              case 1:\n                                 var10008 = 81;\n                                 break;\n                              case 2:\n                                 var10008 = 80;\n                                 break;\n                              case 3:\n                                 var10008 = 68;\n                                 break;\n                              default:\n                                 var10008 = 11;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var8 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 71;\n                     break;\n                  case 1:\n                     var10008 = 81;\n                     break;\n                  case 2:\n                     var10008 = 80;\n                     break;\n                  case 3:\n                     var10008 = 68;\n                     break;\n                  default:\n                     var10008 = 11;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ak.java",
    "content": "public interface ak {\n   String a();\n\n   String b();\n\n   String c();\n\n   double d();\n\n   Double e();\n}\n"
  },
  {
    "path": "testData/obfuscated/al.java",
    "content": "import java.text.DecimalFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\npublic class al implements Comparable<al> {\n   private final ak a;\n   private final List<n<Date, Double>> b = new ArrayList();\n   private final List<n<Date, Double>> c = new ArrayList();\n   private double d = 0.0;\n   private double e = 0.0;\n   private double f = 0.0;\n   private static final String[] g;\n\n   public al(ak var1) {\n      this.a = var1;\n   }\n\n   protected static long a(Date param0, Date param1, TimeUnit param2) {\n      // $FF: Couldn't be decompiled\n   }\n\n   protected static <T> T a(List<T> param0) {\n      // $FF: Couldn't be decompiled\n   }\n\n   protected static <T> T b(List<T> param0) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public void a(double param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public ak a() {\n      return this.a;\n   }\n\n   public List<n<Date, Double>> b() {\n      return this.b;\n   }\n\n   public List<n<Date, Double>> c() {\n      return this.c;\n   }\n\n   public double d() {\n      return this.d;\n   }\n\n   public double e() {\n      try {\n         if (this.b.isEmpty()) {\n            return 0.0;\n         }\n      } catch (a_ var1) {\n         throw var1;\n      }\n\n      return this.e / (double)this.b.size();\n   }\n\n   public double f() {\n      try {\n         if (this.c.isEmpty()) {\n            return 0.0;\n         }\n      } catch (a_ var1) {\n         throw var1;\n      }\n\n      return this.f / (double)this.c.size();\n   }\n\n   public String toString() {\n      StringBuilder var1 = new StringBuilder(this.a.a());\n\n      try {\n         var1.append(g[2]);\n         var1.append(this.a.b());\n         if (this.a.c() != null) {\n            var1.append(g[1]);\n            var1.append(this.a.c());\n            var1.append(\"]\");\n         }\n      } catch (a_ var2) {\n         throw var2;\n      }\n\n      var1.append(g[0]);\n      var1.append(DecimalFormat.getNumberInstance().format(this.d()));\n      var1.append(\" \");\n      var1.append(DecimalFormat.getNumberInstance().format(this.e()));\n      var1.append(\" \");\n      var1.append(DecimalFormat.getNumberInstance().format(this.f()));\n      return var1.toString();\n   }\n\n   protected static boolean a(Object param0, Object param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public int a(al var1) {\n      try {\n         if (var1 == null) {\n            return 1;\n         }\n      } catch (a_ var2) {\n         throw var2;\n      }\n\n      try {\n         if (a(this.a().a(), var1.a().a())) {\n            return this.a().b().compareTo(var1.a().b());\n         }\n      } catch (a_ var3) {\n         throw var3;\n      }\n\n      return this.a().a().compareTo(var1.a().a());\n   }\n\n   static {\n      String[] var10000 = new String[3];\n      char[] var10003 = \"\\u0007Y\".toCharArray();\n      int var10005 = var10003.length;\n      int var1 = 0;\n      char[] var10004 = var10003;\n      int var2 = var10005;\n      char[] var4;\n      int var10006;\n      char var10007;\n      byte var10008;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 61;\n               break;\n            case 1:\n               var10008 = 121;\n               break;\n            case 2:\n               var10008 = 103;\n               break;\n            case 3:\n               var10008 = 113;\n               break;\n            default:\n               var10008 = 123;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label127: {\n               var10000[0] = (new String(var10003)).intern();\n               var10003 = \"\\u001d\\\"\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label127;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 61;\n                        break;\n                     case 1:\n                        var10008 = 121;\n                        break;\n                     case 2:\n                        var10008 = 103;\n                        break;\n                     case 3:\n                        var10008 = 113;\n                        break;\n                     default:\n                        var10008 = 123;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[1] = (new String(var10004)).intern();\n            var10003 = \"\\u001dTG\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  var10000[2] = (new String(var10003)).intern();\n                  g = var10000;\n                  return;\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n            }\n\n            while(true) {\n               var10007 = var4[var10006];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 61;\n                     break;\n                  case 1:\n                     var10008 = 121;\n                     break;\n                  case 2:\n                     var10008 = 103;\n                     break;\n                  case 3:\n                     var10008 = 113;\n                     break;\n                  default:\n                     var10008 = 123;\n               }\n\n               var4[var10006] = (char)(var10007 ^ var10008);\n               ++var1;\n               if (var2 == 0) {\n                  var10006 = var2;\n                  var4 = var10004;\n               } else {\n                  if (var2 <= var1) {\n                     var10000[2] = (new String(var10004)).intern();\n                     g = var10000;\n                     return;\n                  }\n\n                  var4 = var10004;\n                  var10006 = var1;\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 61;\n               break;\n            case 1:\n               var10008 = 121;\n               break;\n            case 2:\n               var10008 = 103;\n               break;\n            case 3:\n               var10008 = 113;\n               break;\n            default:\n               var10008 = 123;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 61;\n                     break;\n                  case 1:\n                     var10008 = 121;\n                     break;\n                  case 2:\n                     var10008 = 103;\n                     break;\n                  case 3:\n                     var10008 = 113;\n                     break;\n                  default:\n                     var10008 = 123;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label65: {\n                     var10000[0] = (new String(var10004)).intern();\n                     var10003 = \"\\u001d\\\"\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label65;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 61;\n                              break;\n                           case 1:\n                              var10008 = 121;\n                              break;\n                           case 2:\n                              var10008 = 103;\n                              break;\n                           case 3:\n                              var10008 = 113;\n                              break;\n                           default:\n                              var10008 = 123;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[1] = (new String(var10004)).intern();\n                  var10003 = \"\\u001dTG\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[2] = (new String(var10003)).intern();\n                        g = var10000;\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 61;\n                           break;\n                        case 1:\n                           var10008 = 121;\n                           break;\n                        case 2:\n                           var10008 = 103;\n                           break;\n                        case 3:\n                           var10008 = 113;\n                           break;\n                        default:\n                           var10008 = 123;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[2] = (new String(var10004)).intern();\n                           g = var10000;\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 61;\n                     break;\n                  case 1:\n                     var10008 = 121;\n                     break;\n                  case 2:\n                     var10008 = 103;\n                     break;\n                  case 3:\n                     var10008 = 113;\n                     break;\n                  default:\n                     var10008 = 123;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/am.java",
    "content": "public interface am {\n   void a(k<ak> var1);\n}\n"
  },
  {
    "path": "testData/obfuscated/an.java",
    "content": "import java.lang.management.GarbageCollectorMXBean;\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.MemoryMXBean;\nimport java.lang.management.MemoryPoolMXBean;\nimport java.lang.management.OperatingSystemMXBean;\nimport java.lang.management.ThreadMXBean;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\n\n@aa(\n   a = {am.class}\n)\npublic class an implements am {\n   private OperatingSystemMXBean a = ManagementFactory.getOperatingSystemMXBean();\n   private MemoryMXBean b = ManagementFactory.getMemoryMXBean();\n   private List<MemoryPoolMXBean> c = ManagementFactory.getMemoryPoolMXBeans();\n   private ThreadMXBean d = ManagementFactory.getThreadMXBean();\n   private List<GarbageCollectorMXBean> e = ManagementFactory.getGarbageCollectorMXBeans();\n   private Map<Long, ao> f = Collections.synchronizedMap(new TreeMap());\n   private ak g;\n   private List<aw> h;\n   private ak i;\n   private ak j;\n   public static boolean k;\n   private static final String[] l;\n\n   public an() {\n      this.g = new at(this, l[2], l[1]);\n      this.i = new au(this, l[0], l[5]);\n      this.j = new av(this, l[4], l[3]);\n   }\n\n   private List<aw> a() {\n      boolean var4 = k;\n\n      List var10000;\n      label45: {\n         try {\n            var10000 = this.h;\n            if (var4) {\n               return var10000;\n            }\n\n            if (var10000 != null) {\n               break label45;\n            }\n         } catch (a_ var6) {\n            throw var6;\n         }\n\n         ArrayList var1 = new ArrayList();\n         Iterator var2 = this.c.iterator();\n\n         while(var2.hasNext()) {\n            MemoryPoolMXBean var3 = (MemoryPoolMXBean)var2.next();\n\n            try {\n               var1.add(new aw(this, var3));\n               if (var4) {\n                  break label45;\n               }\n\n               if (var4) {\n                  break;\n               }\n            } catch (a_ var5) {\n               throw var5;\n            }\n         }\n\n         this.h = var1;\n      }\n\n      var10000 = this.h;\n      return var10000;\n   }\n\n   public void a(k<ak> var1) {\n      var1.a((Object)this.g);\n      var1.a((Collection)this.a());\n      var1.a((Object)this.i);\n      var1.a((Object)this.j);\n   }\n\n   public List<ao> b() {\n      ArrayList var1 = new ArrayList(this.f.values());\n      Collections.sort(var1);\n      return var1;\n   }\n\n   static MemoryMXBean a(an var0) {\n      return var0.b;\n   }\n\n   static Map b(an var0) {\n      return var0.f;\n   }\n\n   static ThreadMXBean c(an var0) {\n      return var0.d;\n   }\n\n   static OperatingSystemMXBean d(an var0) {\n      return var0.a;\n   }\n\n   static List e(an var0) {\n      return var0.e;\n   }\n\n   static {\n      String[] var10000;\n      int var1;\n      int var2;\n      char[] var10003;\n      char[] var10004;\n      char[] var4;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label304: {\n         var10000 = new String[6];\n         var10003 = \"\\u0006\\b\\u000e\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= 1) {\n            var4 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= var1) {\n               break label304;\n            }\n\n            var4 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var4[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 76;\n                  break;\n               case 1:\n                  var10008 = 94;\n                  break;\n               case 2:\n                  var10008 = 67;\n                  break;\n               case 3:\n                  var10008 = 50;\n                  break;\n               default:\n                  var10008 = 117;\n            }\n\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"\\u0004;\\\"B\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var2 = var10005;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 76;\n               break;\n            case 1:\n               var10008 = 94;\n               break;\n            case 2:\n               var10008 = 67;\n               break;\n            case 3:\n               var10008 = 50;\n               break;\n            default:\n               var10008 = 117;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label347: {\n               var10000[1] = (new String(var10003)).intern();\n               var10003 = \"\\u0006\\b\\u000e\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label347;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 76;\n                        break;\n                     case 1:\n                        var10008 = 94;\n                        break;\n                     case 2:\n                        var10008 = 67;\n                        break;\n                     case 3:\n                        var10008 = 50;\n                        break;\n                     default:\n                        var10008 = 117;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[2] = (new String(var10004)).intern();\n            var10003 = \"\\u000b\\u001d\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 76;\n                     break;\n                  case 1:\n                     var10008 = 94;\n                     break;\n                  case 2:\n                     var10008 = 67;\n                     break;\n                  case 3:\n                     var10008 = 50;\n                     break;\n                  default:\n                     var10008 = 117;\n               }\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  label415: {\n                     var10000[3] = (new String(var10003)).intern();\n                     var10003 = \"\\u0006\\b\\u000e\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label415;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 76;\n                              break;\n                           case 1:\n                              var10008 = 94;\n                              break;\n                           case 2:\n                              var10008 = 67;\n                              break;\n                           case 3:\n                              var10008 = 50;\n                              break;\n                           default:\n                              var10008 = 117;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[4] = (new String(var10004)).intern();\n                  var10003 = \"\\u000f\\u000e\\u0016\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[5] = (new String(var10003)).intern();\n                        l = var10000;\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 76;\n                           break;\n                        case 1:\n                           var10008 = 94;\n                           break;\n                        case 2:\n                           var10008 = 67;\n                           break;\n                        case 3:\n                           var10008 = 50;\n                           break;\n                        default:\n                           var10008 = 117;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[5] = (new String(var10004)).intern();\n                           l = var10000;\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 76;\n                     break;\n                  case 1:\n                     var10008 = 94;\n                     break;\n                  case 2:\n                     var10008 = 67;\n                     break;\n                  case 3:\n                     var10008 = 50;\n                     break;\n                  default:\n                     var10008 = 117;\n               }\n            }\n\n            while(true) {\n               while(true) {\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                     var10007 = var10004[var2];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 76;\n                           break;\n                        case 1:\n                           var10008 = 94;\n                           break;\n                        case 2:\n                           var10008 = 67;\n                           break;\n                        case 3:\n                           var10008 = 50;\n                           break;\n                        default:\n                           var10008 = 117;\n                     }\n                  } else {\n                     if (var2 <= var1) {\n                        label523: {\n                           var10000[3] = (new String(var10004)).intern();\n                           var10003 = \"\\u0006\\b\\u000e\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label523;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 76;\n                                    break;\n                                 case 1:\n                                    var10008 = 94;\n                                    break;\n                                 case 2:\n                                    var10008 = 67;\n                                    break;\n                                 case 3:\n                                    var10008 = 50;\n                                    break;\n                                 default:\n                                    var10008 = 117;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[4] = (new String(var10004)).intern();\n                        var10003 = \"\\u000f\\u000e\\u0016\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[5] = (new String(var10003)).intern();\n                              l = var10000;\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 76;\n                                 break;\n                              case 1:\n                                 var10008 = 94;\n                                 break;\n                              case 2:\n                                 var10008 = 67;\n                                 break;\n                              case 3:\n                                 var10008 = 50;\n                                 break;\n                              default:\n                                 var10008 = 117;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[5] = (new String(var10004)).intern();\n                                 l = var10000;\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                     var10007 = var10004[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 76;\n                           break;\n                        case 1:\n                           var10008 = 94;\n                           break;\n                        case 2:\n                           var10008 = 67;\n                           break;\n                        case 3:\n                           var10008 = 50;\n                           break;\n                        default:\n                           var10008 = 117;\n                     }\n                  }\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 76;\n               break;\n            case 1:\n               var10008 = 94;\n               break;\n            case 2:\n               var10008 = 67;\n               break;\n            case 3:\n               var10008 = 50;\n               break;\n            default:\n               var10008 = 117;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 76;\n                     break;\n                  case 1:\n                     var10008 = 94;\n                     break;\n                  case 2:\n                     var10008 = 67;\n                     break;\n                  case 3:\n                     var10008 = 50;\n                     break;\n                  default:\n                     var10008 = 117;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label143: {\n                     var10000[1] = (new String(var10004)).intern();\n                     var10003 = \"\\u0006\\b\\u000e\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label143;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 76;\n                              break;\n                           case 1:\n                              var10008 = 94;\n                              break;\n                           case 2:\n                              var10008 = 67;\n                              break;\n                           case 3:\n                              var10008 = 50;\n                              break;\n                           default:\n                              var10008 = 117;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[2] = (new String(var10004)).intern();\n                  var10003 = \"\\u000b\\u001d\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 76;\n                           break;\n                        case 1:\n                           var10008 = 94;\n                           break;\n                        case 2:\n                           var10008 = 67;\n                           break;\n                        case 3:\n                           var10008 = 50;\n                           break;\n                        default:\n                           var10008 = 117;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        label187: {\n                           var10000[3] = (new String(var10003)).intern();\n                           var10003 = \"\\u0006\\b\\u000e\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label187;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 76;\n                                    break;\n                                 case 1:\n                                    var10008 = 94;\n                                    break;\n                                 case 2:\n                                    var10008 = 67;\n                                    break;\n                                 case 3:\n                                    var10008 = 50;\n                                    break;\n                                 default:\n                                    var10008 = 117;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[4] = (new String(var10004)).intern();\n                        var10003 = \"\\u000f\\u000e\\u0016\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[5] = (new String(var10003)).intern();\n                              l = var10000;\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 76;\n                                 break;\n                              case 1:\n                                 var10008 = 94;\n                                 break;\n                              case 2:\n                                 var10008 = 67;\n                                 break;\n                              case 3:\n                                 var10008 = 50;\n                                 break;\n                              default:\n                                 var10008 = 117;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[5] = (new String(var10004)).intern();\n                                 l = var10000;\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 76;\n                           break;\n                        case 1:\n                           var10008 = 94;\n                           break;\n                        case 2:\n                           var10008 = 67;\n                           break;\n                        case 3:\n                           var10008 = 50;\n                           break;\n                        default:\n                           var10008 = 117;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                           var10007 = var10004[var2];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 76;\n                                 break;\n                              case 1:\n                                 var10008 = 94;\n                                 break;\n                              case 2:\n                                 var10008 = 67;\n                                 break;\n                              case 3:\n                                 var10008 = 50;\n                                 break;\n                              default:\n                                 var10008 = 117;\n                           }\n                        } else {\n                           if (var2 <= var1) {\n                              label107: {\n                                 var10000[3] = (new String(var10004)).intern();\n                                 var10003 = \"\\u0006\\b\\u000e\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= 1) {\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var2 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label107;\n                                    }\n\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var4[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 76;\n                                          break;\n                                       case 1:\n                                          var10008 = 94;\n                                          break;\n                                       case 2:\n                                          var10008 = 67;\n                                          break;\n                                       case 3:\n                                          var10008 = 50;\n                                          break;\n                                       default:\n                                          var10008 = 117;\n                                    }\n\n                                    var4[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var2 == 0) {\n                                       var10006 = var2;\n                                       var4 = var10004;\n                                    } else {\n                                       if (var2 <= var1) {\n                                          break;\n                                       }\n\n                                       var4 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[4] = (new String(var10004)).intern();\n                              var10003 = \"\\u000f\\u000e\\u0016\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= 1) {\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              } else {\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= var1) {\n                                    var10000[5] = (new String(var10003)).intern();\n                                    l = var10000;\n                                    return;\n                                 }\n\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              }\n\n                              while(true) {\n                                 var10007 = var4[var10006];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 76;\n                                       break;\n                                    case 1:\n                                       var10008 = 94;\n                                       break;\n                                    case 2:\n                                       var10008 = 67;\n                                       break;\n                                    case 3:\n                                       var10008 = 50;\n                                       break;\n                                    default:\n                                       var10008 = 117;\n                                 }\n\n                                 var4[var10006] = (char)(var10007 ^ var10008);\n                                 ++var1;\n                                 if (var2 == 0) {\n                                    var10006 = var2;\n                                    var4 = var10004;\n                                 } else {\n                                    if (var2 <= var1) {\n                                       var10000[5] = (new String(var10004)).intern();\n                                       l = var10000;\n                                       return;\n                                    }\n\n                                    var4 = var10004;\n                                    var10006 = var1;\n                                 }\n                              }\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 76;\n                                 break;\n                              case 1:\n                                 var10008 = 94;\n                                 break;\n                              case 2:\n                                 var10008 = 67;\n                                 break;\n                              case 3:\n                                 var10008 = 50;\n                                 break;\n                              default:\n                                 var10008 = 117;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 76;\n                     break;\n                  case 1:\n                     var10008 = 94;\n                     break;\n                  case 2:\n                     var10008 = 67;\n                     break;\n                  case 3:\n                     var10008 = 50;\n                     break;\n                  default:\n                     var10008 = 117;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ao.java",
    "content": "public class ao implements Comparable<ao> {\n   protected String a;\n   protected Thread.State b;\n   protected double c;\n   protected long d;\n   protected long e;\n\n   public String a() {\n      return this.a;\n   }\n\n   public Thread.State b() {\n      return this.b;\n   }\n\n   public double c() {\n      return this.c;\n   }\n\n   public int a(ao param1) {\n      // $FF: Couldn't be decompiled\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ap.java",
    "content": "public abstract class ap implements ak {\n   private final String a;\n   private final String b;\n   public static int c;\n   private static final String d;\n\n   public ap(String var1, String var2) {\n      this.b = var1;\n      this.a = var2;\n   }\n\n   public String b() {\n      return this.a;\n   }\n\n   public String a() {\n      return this.b;\n   }\n\n   public int hashCode() {\n      return (this.b + this.a).hashCode();\n   }\n\n   public boolean equals(Object param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public String toString() {\n      return this.b + d + this.a;\n   }\n\n   static {\n      char[] var10000 = \"K\\u000eX\".toCharArray();\n      int var10002 = var10000.length;\n      int var1 = 0;\n      char[] var10001 = var10000;\n      int var2 = var10002;\n      int var10003;\n      char[] var4;\n      if (var10002 <= 1) {\n         var4 = var10000;\n         var10003 = var1;\n      } else {\n         var10001 = var10000;\n         var2 = var10002;\n         if (var10002 <= var1) {\n            d = (new String(var10000)).intern();\n            return;\n         }\n\n         var4 = var10000;\n         var10003 = var1;\n      }\n\n      while(true) {\n         char var10004 = var4[var10003];\n         byte var10005;\n         switch (var1 % 5) {\n            case 0:\n               var10005 = 107;\n               break;\n            case 1:\n               var10005 = 35;\n               break;\n            case 2:\n               var10005 = 120;\n               break;\n            case 3:\n               var10005 = 65;\n               break;\n            default:\n               var10005 = 23;\n         }\n\n         var4[var10003] = (char)(var10004 ^ var10005);\n         ++var1;\n         if (var2 == 0) {\n            var10003 = var2;\n            var4 = var10001;\n         } else {\n            if (var2 <= var1) {\n               d = (new String(var10001)).intern();\n               return;\n            }\n\n            var4 = var10001;\n            var10003 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/aq.java",
    "content": "public class aq extends ap {\n   private volatile long d;\n   private volatile long e;\n   private final String f;\n   private static final long g = 4611686018427387903L;\n   private final Double h;\n\n   public aq(String var1, String var2, String var3, Double var4) {\n      super(var1, var2);\n      this.f = var3;\n      this.h = var4;\n   }\n\n   public String c() {\n      return this.f;\n   }\n\n   public void a(long param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public double d() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public Double e() {\n      return this.h;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ar.java",
    "content": "public class ar extends ap {\n   private final String d;\n   private final double e;\n\n   public ar(String var1, String var2, String var3, double var4) {\n      super(var1, var2);\n      this.d = var3;\n      this.e = var4;\n   }\n\n   public String c() {\n      return this.d;\n   }\n\n   public double d() {\n      return this.e;\n   }\n\n   public Double e() {\n      return null;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/as.java",
    "content": "import java.util.concurrent.TimeUnit;\n\npublic class as extends ap {\n   private volatile long d = 0L;\n   private volatile long e;\n   private final TimeUnit f;\n   private final Double g;\n   private String h;\n   private double i;\n\n   public as(String var1, String var2, TimeUnit var3, Double var4, String var5) {\n      super(var1, var2);\n      this.f = var3;\n      this.h = var5;\n      this.g = var4;\n      this.e = System.currentTimeMillis();\n   }\n\n   public String c() {\n      return this.h;\n   }\n\n   public void a() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public void a(long var1) {\n      try {\n         if (9223372036854775797L - var1 > this.d) {\n            this.d += var1;\n         }\n\n      } catch (a_ var3) {\n         throw var3;\n      }\n   }\n\n   public double d() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public double b() {\n      return this.i;\n   }\n\n   public Double e() {\n      return this.g;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/at.java",
    "content": "class at extends ap {\n   final an d;\n   private static final String e;\n\n   at(an var1, String var2, String var3) {\n      super(var2, var3);\n      this.d = var1;\n   }\n\n   public double d() {\n      return (double)an.a(this.d).getHeapMemoryUsage().getUsed() / 1024.0 / 1024.0;\n   }\n\n   public String c() {\n      return e;\n   }\n\n   public Double e() {\n      return (double)an.a(this.d).getHeapMemoryUsage().getMax() / 1024.0 / 1024.0;\n   }\n\n   static {\n      char[] var10000 = \"$b\".toCharArray();\n      int var10002 = var10000.length;\n      int var1 = 0;\n      char[] var10001 = var10000;\n      int var2 = var10002;\n      int var10003;\n      char[] var4;\n      if (var10002 <= 1) {\n         var4 = var10000;\n         var10003 = var1;\n      } else {\n         var10001 = var10000;\n         var2 = var10002;\n         if (var10002 <= var1) {\n            e = (new String(var10000)).intern();\n            return;\n         }\n\n         var4 = var10000;\n         var10003 = var1;\n      }\n\n      while(true) {\n         char var10004 = var4[var10003];\n         byte var10005;\n         switch (var1 % 5) {\n            case 0:\n               var10005 = 105;\n               break;\n            case 1:\n               var10005 = 32;\n               break;\n            case 2:\n               var10005 = 18;\n               break;\n            case 3:\n               var10005 = 31;\n               break;\n            default:\n               var10005 = 120;\n         }\n\n         var4[var10003] = (char)(var10004 ^ var10005);\n         ++var1;\n         if (var2 == 0) {\n            var10003 = var2;\n            var4 = var10001;\n         } else {\n            if (var2 <= var1) {\n               e = (new String(var10001)).intern();\n               return;\n            }\n\n            var4 = var10001;\n            var10003 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/au.java",
    "content": "class au extends ap {\n   private long d;\n   private long e;\n   final an f;\n\n   au(an var1, String var2, String var3) {\n      super(var2, var3);\n      this.f = var1;\n      this.d = 0L;\n      this.e = 0L;\n   }\n\n   public double d() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public String c() {\n      return \"%\";\n   }\n\n   public Double e() {\n      return 100.0;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/av.java",
    "content": "class av extends ap {\n   private long d;\n   private long e;\n   final an f;\n\n   av(an var1, String var2, String var3) {\n      super(var2, var3);\n      this.f = var1;\n      this.d = 0L;\n      this.e = 0L;\n   }\n\n   public double d() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public String c() {\n      return \"%\";\n   }\n\n   public Double e() {\n      return 100.0;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/aw.java",
    "content": "import java.lang.management.MemoryPoolMXBean;\n\nclass aw extends ap {\n   private MemoryPoolMXBean d;\n   final an e;\n   private static final String[] f;\n\n   public aw(an var1, MemoryPoolMXBean var2) {\n      super(f[1], f[0] + var2.getName());\n      this.e = var1;\n      this.d = var2;\n   }\n\n   public double d() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public String c() {\n      return f[2];\n   }\n\n   public Double e() {\n      // $FF: Couldn't be decompiled\n   }\n\n   static {\n      String[] var10000 = new String[3];\n      char[] var10003 = \"6\\u0000ec\".toCharArray();\n      int var10005 = var10003.length;\n      int var1 = 0;\n      char[] var10004 = var10003;\n      int var2 = var10005;\n      char[] var4;\n      int var10006;\n      char var10007;\n      byte var10008;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 123;\n               break;\n            case 1:\n               var10008 = 69;\n               break;\n            case 2:\n               var10008 = 40;\n               break;\n            case 3:\n               var10008 = 78;\n               break;\n            default:\n               var10008 = 31;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label127: {\n               var10000[0] = (new String(var10003)).intern();\n               var10003 = \"1\\u0013e\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label127;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 123;\n                        break;\n                     case 1:\n                        var10008 = 69;\n                        break;\n                     case 2:\n                        var10008 = 40;\n                        break;\n                     case 3:\n                        var10008 = 78;\n                        break;\n                     default:\n                        var10008 = 31;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[1] = (new String(var10004)).intern();\n            var10003 = \"6\\u0007\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  var10000[2] = (new String(var10003)).intern();\n                  f = var10000;\n                  return;\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n            }\n\n            while(true) {\n               var10007 = var4[var10006];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 123;\n                     break;\n                  case 1:\n                     var10008 = 69;\n                     break;\n                  case 2:\n                     var10008 = 40;\n                     break;\n                  case 3:\n                     var10008 = 78;\n                     break;\n                  default:\n                     var10008 = 31;\n               }\n\n               var4[var10006] = (char)(var10007 ^ var10008);\n               ++var1;\n               if (var2 == 0) {\n                  var10006 = var2;\n                  var4 = var10004;\n               } else {\n                  if (var2 <= var1) {\n                     var10000[2] = (new String(var10004)).intern();\n                     f = var10000;\n                     return;\n                  }\n\n                  var4 = var10004;\n                  var10006 = var1;\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 123;\n               break;\n            case 1:\n               var10008 = 69;\n               break;\n            case 2:\n               var10008 = 40;\n               break;\n            case 3:\n               var10008 = 78;\n               break;\n            default:\n               var10008 = 31;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 123;\n                     break;\n                  case 1:\n                     var10008 = 69;\n                     break;\n                  case 2:\n                     var10008 = 40;\n                     break;\n                  case 3:\n                     var10008 = 78;\n                     break;\n                  default:\n                     var10008 = 31;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label65: {\n                     var10000[0] = (new String(var10004)).intern();\n                     var10003 = \"1\\u0013e\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label65;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 123;\n                              break;\n                           case 1:\n                              var10008 = 69;\n                              break;\n                           case 2:\n                              var10008 = 40;\n                              break;\n                           case 3:\n                              var10008 = 78;\n                              break;\n                           default:\n                              var10008 = 31;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[1] = (new String(var10004)).intern();\n                  var10003 = \"6\\u0007\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[2] = (new String(var10003)).intern();\n                        f = var10000;\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 123;\n                           break;\n                        case 1:\n                           var10008 = 69;\n                           break;\n                        case 2:\n                           var10008 = 40;\n                           break;\n                        case 3:\n                           var10008 = 78;\n                           break;\n                        default:\n                           var10008 = 31;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[2] = (new String(var10004)).intern();\n                           f = var10000;\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 123;\n                     break;\n                  case 1:\n                     var10008 = 69;\n                     break;\n                  case 2:\n                     var10008 = 40;\n                     break;\n                  case 3:\n                     var10008 = 78;\n                     break;\n                  default:\n                     var10008 = 31;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ax.java",
    "content": "import java.util.concurrent.TimeUnit;\n\npublic class ax {\n   private long a = 0L;\n   private static final String[] b;\n\n   public static ax a() {\n      return new ax();\n   }\n\n   private ax() {\n      this.b();\n   }\n\n   public void b() {\n      this.a = System.nanoTime();\n   }\n\n   public long c() {\n      return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - this.a);\n   }\n\n   public String d() {\n      return this.c() + b[0];\n   }\n\n   public String a(boolean var1) {\n      String var2 = String.valueOf(((double)System.nanoTime() - (double)this.a) / 1000.0);\n\n      try {\n         if (var1) {\n            this.b();\n         }\n\n         return var2;\n      } catch (a_ var3) {\n         throw var3;\n      }\n   }\n\n   public String e() {\n      String var1 = this.c() + b[1];\n      this.b();\n      return var1;\n   }\n\n   static {\n      String[] var10000;\n      int var1;\n      int var2;\n      char[] var10003;\n      char[] var10004;\n      char[] var4;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label51: {\n         var10000 = new String[2];\n         var10003 = \"}|\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= 1) {\n            var4 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= var1) {\n               break label51;\n            }\n\n            var4 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var4[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 16;\n                  break;\n               case 1:\n                  var10008 = 15;\n                  break;\n               case 2:\n                  var10008 = 44;\n                  break;\n               case 3:\n                  var10008 = 84;\n                  break;\n               default:\n                  var10008 = 86;\n            }\n\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"}|\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var2 = var10005;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            var10000[1] = (new String(var10003)).intern();\n            b = var10000;\n            return;\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n      }\n\n      while(true) {\n         var10007 = var4[var10006];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 16;\n               break;\n            case 1:\n               var10008 = 15;\n               break;\n            case 2:\n               var10008 = 44;\n               break;\n            case 3:\n               var10008 = 84;\n               break;\n            default:\n               var10008 = 86;\n         }\n\n         var4[var10006] = (char)(var10007 ^ var10008);\n         ++var1;\n         if (var2 == 0) {\n            var10006 = var2;\n            var4 = var10004;\n         } else {\n            if (var2 <= var1) {\n               var10000[1] = (new String(var10004)).intern();\n               b = var10000;\n               return;\n            }\n\n            var4 = var10004;\n            var10006 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ay.java",
    "content": "public interface ay {\n   void a(a0 var1);\n}\n"
  },
  {
    "path": "testData/obfuscated/az.java",
    "content": "import javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\nimport org.w3c.dom.DOMException;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.ProcessingInstruction;\nimport org.xml.sax.Attributes;\n\nclass az {\n   private Document a;\n   private Node b;\n   private Node c;\n   private ay d;\n   private static final String[] e;\n\n   public az(ay var1, String var2, String var3, Attributes var4) throws ParserConfigurationException {\n      this.d = var1;\n      DocumentBuilderFactory var5 = DocumentBuilderFactory.newInstance();\n      DocumentBuilder var6 = var5.newDocumentBuilder();\n      this.a = var6.newDocument();\n      this.a(var3, var4);\n   }\n\n   private boolean a() {\n      try {\n         if (this.b()) {\n            this.d.a(new a1(this.b));\n            return true;\n         }\n      } catch (DOMException var1) {\n         throw var1;\n      }\n\n      this.c = this.c.getParentNode();\n      return false;\n   }\n\n   private boolean b() {\n      return this.c.equals(this.b);\n   }\n\n   private void a(String param1, Attributes param2) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public Node c() {\n      return this.b;\n   }\n\n   public void a(String var1, String var2, Attributes var3) {\n      this.a(var2, var3);\n   }\n\n   public void a(String var1, String var2) {\n      ProcessingInstruction var3 = this.a.createProcessingInstruction(var1, var2);\n      this.c.appendChild(var3);\n   }\n\n   public boolean b(String var1, String var2) {\n      try {\n         if (!this.c.getNodeName().equals(var2)) {\n            throw new DOMException((short)12, e[0] + var2 + e[1] + this.c.getNodeName());\n         }\n      } catch (DOMException var3) {\n         throw var3;\n      }\n\n      return this.a();\n   }\n\n   public void a(String var1) {\n      this.c.appendChild(this.a.createTextNode(var1));\n   }\n\n   public ay d() {\n      return this.d;\n   }\n\n   static {\n      String[] var10000;\n      int var1;\n      int var2;\n      char[] var10003;\n      char[] var10004;\n      char[] var4;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label51: {\n         var10000 = new String[2];\n         var10003 = \"+\\u0013*w'\\u001b\\u001e;j3^\\u0018!kz\\n\\u001c(5w\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= 1) {\n            var4 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= var1) {\n               break label51;\n            }\n\n            var4 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var4[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 126;\n                  break;\n               case 1:\n                  var10008 = 125;\n                  break;\n               case 2:\n                  var10008 = 79;\n                  break;\n               case 3:\n                  var10008 = 15;\n                  break;\n               default:\n                  var10008 = 87;\n            }\n\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"^\\u00187\\u007f2\\u001d\\t*km^\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var2 = var10005;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            var10000[1] = (new String(var10003)).intern();\n            e = var10000;\n            return;\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n      }\n\n      while(true) {\n         var10007 = var4[var10006];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 126;\n               break;\n            case 1:\n               var10008 = 125;\n               break;\n            case 2:\n               var10008 = 79;\n               break;\n            case 3:\n               var10008 = 15;\n               break;\n            default:\n               var10008 = 87;\n         }\n\n         var4[var10006] = (char)(var10007 ^ var10008);\n         ++var1;\n         if (var2 == 0) {\n            var10006 = var2;\n            var4 = var10004;\n         } else {\n            if (var2 <= var1) {\n               var10000[1] = (new String(var10004)).intern();\n               e = var10000;\n               return;\n            }\n\n            var4 = var10004;\n            var10006 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/b.java",
    "content": "import java.util.Date;\n\npublic class b<K, V> {\n   protected ah a;\n   protected long b;\n   protected long c;\n   protected final K d;\n   protected V e;\n   protected long f;\n   protected long g;\n\n   public ah a() {\n      return this.a;\n   }\n\n   public long b() {\n      return this.a.b();\n   }\n\n   public Date c() {\n      return new Date(this.b);\n   }\n\n   public void a(long var1) {\n      this.b = var1;\n   }\n\n   public Date d() {\n      return new Date(this.c);\n   }\n\n   public Date e() {\n      return new Date(this.f);\n   }\n\n   public void b(long var1) {\n      this.c = var1;\n   }\n\n   public V f() {\n      return this.e;\n   }\n\n   public void a(V var1) {\n      this.e = var1;\n   }\n\n   public b(K var1, V var2, long var3, long var5) {\n      boolean var7 = d.b;\n      super();\n      this.a = new ah();\n      this.b = 0L;\n      this.c = 0L;\n      this.d = var1;\n      this.f = var3;\n      this.g = var5;\n      this.c = System.currentTimeMillis();\n      this.b = this.c;\n      this.e = var2;\n      if (ap.c != 0) {\n         boolean var10000;\n         label18: {\n            try {\n               if (var7) {\n                  var10000 = false;\n                  break label18;\n               }\n            } catch (a_ var8) {\n               throw var8;\n            }\n\n            var10000 = true;\n         }\n\n         d.b = var10000;\n      }\n\n   }\n\n   public long g() {\n      return this.f;\n   }\n\n   public void c(long var1) {\n      this.f = var1;\n   }\n\n   public long h() {\n      return this.g;\n   }\n\n   public void d(long var1) {\n      this.g = var1;\n   }\n\n   public K i() {\n      return this.d;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/ba.java",
    "content": "import java.util.Iterator;\nimport java.util.TimerTask;\n\nclass ba extends TimerTask {\n   final ae a;\n   private static final String[] b;\n\n   private ba(ae var1) {\n      this.a = var1;\n   }\n\n   public void run() {\n      boolean var4 = ae.e;\n      Iterator var1 = ae.a(this.a).iterator();\n\n      while(true) {\n         if (var1.hasNext()) {\n            ac var2 = (ac)var1.next();\n\n            label23: {\n               try {\n                  var2.a();\n               } catch (Exception var6) {\n                  r.a(b[0]).a(b[1], var2.getClass().getName()).a();\n                  break label23;\n               }\n\n               if (var4) {\n                  break;\n               }\n            }\n\n            if (!var4) {\n               continue;\n            }\n\n            int var5 = ap.c;\n            ++var5;\n            ap.c = var5;\n         }\n\n         ae.a(this.a, System.currentTimeMillis());\n         break;\n      }\n\n   }\n\n   ba(ae var1, af var2) {\n      this(var1);\n   }\n\n   static {\n      String[] var10000;\n      int var1;\n      int var2;\n      char[] var10003;\n      char[] var10004;\n      char[] var4;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label51: {\n         var10000 = new String[2];\n         var10003 = \"\\b[\\u0006\\u001d\\u001b\\u0000D\\r\\u001a\\u0016(y\\u0002\\u001c\\t\\u000bL\\n\\u0003\\u0007)\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= 1) {\n            var4 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= var1) {\n               break label51;\n            }\n\n            var4 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var4[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 77;\n                  break;\n               case 1:\n                  var10008 = 45;\n                  break;\n               case 2:\n                  var10008 = 99;\n                  break;\n               case 3:\n                  var10008 = 111;\n                  break;\n               default:\n                  var10008 = 98;\n            }\n\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \".A\\u0002\\u001c\\u0011\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var2 = var10005;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            var10000[1] = (new String(var10003)).intern();\n            b = var10000;\n            return;\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n      }\n\n      while(true) {\n         var10007 = var4[var10006];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 77;\n               break;\n            case 1:\n               var10008 = 45;\n               break;\n            case 2:\n               var10008 = 99;\n               break;\n            case 3:\n               var10008 = 111;\n               break;\n            default:\n               var10008 = 98;\n         }\n\n         var4[var10006] = (char)(var10007 ^ var10008);\n         ++var1;\n         if (var2 == 0) {\n            var10006 = var2;\n            var4 = var10004;\n         } else {\n            if (var2 <= var1) {\n               var10000[1] = (new String(var10004)).intern();\n               b = var10000;\n               return;\n            }\n\n            var4 = var10004;\n            var10006 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/bb.java",
    "content": "import org.xhtmlrenderer.extend.FSImage;\nimport org.xhtmlrenderer.extend.ReplacedElement;\nimport org.xhtmlrenderer.extend.UserAgentCallback;\nimport org.xhtmlrenderer.layout.LayoutContext;\nimport org.xhtmlrenderer.pdf.ITextOutputDevice;\nimport org.xhtmlrenderer.pdf.ITextReplacedElementFactory;\nimport org.xhtmlrenderer.render.BlockBox;\n\npublic class bb extends ITextReplacedElementFactory {\n   public static boolean b;\n   private static final String[] a;\n\n   public bb(ITextOutputDevice var1) {\n      super(var1);\n   }\n\n   public ReplacedElement createReplacedElement(LayoutContext param1, BlockBox param2, UserAgentCallback param3, int param4, int param5) {\n      // $FF: Couldn't be decompiled\n   }\n\n   private n<Integer, Integer> a(int param1, int param2, FSImage param3) {\n      // $FF: Couldn't be decompiled\n   }\n\n   static {\n      String[] var10000 = new String[5];\n      char[] var10003 = \"u\\u0003\\u000e\".toCharArray();\n      int var10005 = var10003.length;\n      int var1 = 0;\n      char[] var10004 = var10003;\n      int var2 = var10005;\n      char[] var4;\n      int var10006;\n      char var10007;\n      byte var10008;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 28;\n               break;\n            case 1:\n               var10008 = 110;\n               break;\n            case 2:\n               var10008 = 105;\n               break;\n            case 3:\n               var10008 = 46;\n               break;\n            default:\n               var10008 = 33;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label316: {\n               var10000[0] = (new String(var10003)).intern();\n               var10003 = \"o\\u001c\\n\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label316;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 28;\n                        break;\n                     case 1:\n                        var10008 = 110;\n                        break;\n                     case 2:\n                        var10008 = 105;\n                        break;\n                     case 3:\n                        var10008 = 46;\n                        break;\n                     default:\n                        var10008 = 33;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[1] = (new String(var10004)).intern();\n            var10003 = \"h\\u0017\\u0019K\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 28;\n                     break;\n                  case 1:\n                     var10008 = 110;\n                     break;\n                  case 2:\n                     var10008 = 105;\n                     break;\n                  case 3:\n                     var10008 = 46;\n                     break;\n                  default:\n                     var10008 = 33;\n               }\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  label384: {\n                     var10000[2] = (new String(var10003)).intern();\n                     var10003 = \"\\u007f\\u0001\\rK\\u0010.V\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label384;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 28;\n                              break;\n                           case 1:\n                              var10008 = 110;\n                              break;\n                           case 2:\n                              var10008 = 105;\n                              break;\n                           case 3:\n                              var10008 = 46;\n                              break;\n                           default:\n                              var10008 = 33;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[3] = (new String(var10004)).intern();\n                  var10003 = \"o\\u001c\\n\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[4] = (new String(var10003)).intern();\n                        a = var10000;\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 28;\n                           break;\n                        case 1:\n                           var10008 = 110;\n                           break;\n                        case 2:\n                           var10008 = 105;\n                           break;\n                        case 3:\n                           var10008 = 46;\n                           break;\n                        default:\n                           var10008 = 33;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[4] = (new String(var10004)).intern();\n                           a = var10000;\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 28;\n                     break;\n                  case 1:\n                     var10008 = 110;\n                     break;\n                  case 2:\n                     var10008 = 105;\n                     break;\n                  case 3:\n                     var10008 = 46;\n                     break;\n                  default:\n                     var10008 = 33;\n               }\n            }\n\n            while(true) {\n               while(true) {\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                     var10007 = var10004[var2];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 28;\n                           break;\n                        case 1:\n                           var10008 = 110;\n                           break;\n                        case 2:\n                           var10008 = 105;\n                           break;\n                        case 3:\n                           var10008 = 46;\n                           break;\n                        default:\n                           var10008 = 33;\n                     }\n                  } else {\n                     if (var2 <= var1) {\n                        label492: {\n                           var10000[2] = (new String(var10004)).intern();\n                           var10003 = \"\\u007f\\u0001\\rK\\u0010.V\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label492;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 28;\n                                    break;\n                                 case 1:\n                                    var10008 = 110;\n                                    break;\n                                 case 2:\n                                    var10008 = 105;\n                                    break;\n                                 case 3:\n                                    var10008 = 46;\n                                    break;\n                                 default:\n                                    var10008 = 33;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"o\\u001c\\n\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[4] = (new String(var10003)).intern();\n                              a = var10000;\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 28;\n                                 break;\n                              case 1:\n                                 var10008 = 110;\n                                 break;\n                              case 2:\n                                 var10008 = 105;\n                                 break;\n                              case 3:\n                                 var10008 = 46;\n                                 break;\n                              default:\n                                 var10008 = 33;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 a = var10000;\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                     var10007 = var10004[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 28;\n                           break;\n                        case 1:\n                           var10008 = 110;\n                           break;\n                        case 2:\n                           var10008 = 105;\n                           break;\n                        case 3:\n                           var10008 = 46;\n                           break;\n                        default:\n                           var10008 = 33;\n                     }\n                  }\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 28;\n               break;\n            case 1:\n               var10008 = 110;\n               break;\n            case 2:\n               var10008 = 105;\n               break;\n            case 3:\n               var10008 = 46;\n               break;\n            default:\n               var10008 = 33;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 28;\n                     break;\n                  case 1:\n                     var10008 = 110;\n                     break;\n                  case 2:\n                     var10008 = 105;\n                     break;\n                  case 3:\n                     var10008 = 46;\n                     break;\n                  default:\n                     var10008 = 33;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label129: {\n                     var10000[0] = (new String(var10004)).intern();\n                     var10003 = \"o\\u001c\\n\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label129;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 28;\n                              break;\n                           case 1:\n                              var10008 = 110;\n                              break;\n                           case 2:\n                              var10008 = 105;\n                              break;\n                           case 3:\n                              var10008 = 46;\n                              break;\n                           default:\n                              var10008 = 33;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[1] = (new String(var10004)).intern();\n                  var10003 = \"h\\u0017\\u0019K\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 28;\n                           break;\n                        case 1:\n                           var10008 = 110;\n                           break;\n                        case 2:\n                           var10008 = 105;\n                           break;\n                        case 3:\n                           var10008 = 46;\n                           break;\n                        default:\n                           var10008 = 33;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        label173: {\n                           var10000[2] = (new String(var10003)).intern();\n                           var10003 = \"\\u007f\\u0001\\rK\\u0010.V\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label173;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 28;\n                                    break;\n                                 case 1:\n                                    var10008 = 110;\n                                    break;\n                                 case 2:\n                                    var10008 = 105;\n                                    break;\n                                 case 3:\n                                    var10008 = 46;\n                                    break;\n                                 default:\n                                    var10008 = 33;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"o\\u001c\\n\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[4] = (new String(var10003)).intern();\n                              a = var10000;\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 28;\n                                 break;\n                              case 1:\n                                 var10008 = 110;\n                                 break;\n                              case 2:\n                                 var10008 = 105;\n                                 break;\n                              case 3:\n                                 var10008 = 46;\n                                 break;\n                              default:\n                                 var10008 = 33;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 a = var10000;\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 28;\n                           break;\n                        case 1:\n                           var10008 = 110;\n                           break;\n                        case 2:\n                           var10008 = 105;\n                           break;\n                        case 3:\n                           var10008 = 46;\n                           break;\n                        default:\n                           var10008 = 33;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                           var10007 = var10004[var2];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 28;\n                                 break;\n                              case 1:\n                                 var10008 = 110;\n                                 break;\n                              case 2:\n                                 var10008 = 105;\n                                 break;\n                              case 3:\n                                 var10008 = 46;\n                                 break;\n                              default:\n                                 var10008 = 33;\n                           }\n                        } else {\n                           if (var2 <= var1) {\n                              label93: {\n                                 var10000[2] = (new String(var10004)).intern();\n                                 var10003 = \"\\u007f\\u0001\\rK\\u0010.V\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= 1) {\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var2 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label93;\n                                    }\n\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var4[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 28;\n                                          break;\n                                       case 1:\n                                          var10008 = 110;\n                                          break;\n                                       case 2:\n                                          var10008 = 105;\n                                          break;\n                                       case 3:\n                                          var10008 = 46;\n                                          break;\n                                       default:\n                                          var10008 = 33;\n                                    }\n\n                                    var4[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var2 == 0) {\n                                       var10006 = var2;\n                                       var4 = var10004;\n                                    } else {\n                                       if (var2 <= var1) {\n                                          break;\n                                       }\n\n                                       var4 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[3] = (new String(var10004)).intern();\n                              var10003 = \"o\\u001c\\n\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= 1) {\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              } else {\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= var1) {\n                                    var10000[4] = (new String(var10003)).intern();\n                                    a = var10000;\n                                    return;\n                                 }\n\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              }\n\n                              while(true) {\n                                 var10007 = var4[var10006];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 28;\n                                       break;\n                                    case 1:\n                                       var10008 = 110;\n                                       break;\n                                    case 2:\n                                       var10008 = 105;\n                                       break;\n                                    case 3:\n                                       var10008 = 46;\n                                       break;\n                                    default:\n                                       var10008 = 33;\n                                 }\n\n                                 var4[var10006] = (char)(var10007 ^ var10008);\n                                 ++var1;\n                                 if (var2 == 0) {\n                                    var10006 = var2;\n                                    var4 = var10004;\n                                 } else {\n                                    if (var2 <= var1) {\n                                       var10000[4] = (new String(var10004)).intern();\n                                       a = var10000;\n                                       return;\n                                    }\n\n                                    var4 = var10004;\n                                    var10006 = var1;\n                                 }\n                              }\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 28;\n                                 break;\n                              case 1:\n                                 var10008 = 110;\n                                 break;\n                              case 2:\n                                 var10008 = 105;\n                                 break;\n                              case 3:\n                                 var10008 = 46;\n                                 break;\n                              default:\n                                 var10008 = 33;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 28;\n                     break;\n                  case 1:\n                     var10008 = 110;\n                     break;\n                  case 2:\n                     var10008 = 105;\n                     break;\n                  case 3:\n                     var10008 = 46;\n                     break;\n                  default:\n                     var10008 = 33;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/bc.java",
    "content": "import java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport javax.xml.parsers.ParserConfigurationException;\nimport javax.xml.parsers.SAXParser;\nimport javax.xml.parsers.SAXParserFactory;\nimport org.w3c.dom.Node;\nimport org.xml.sax.Attributes;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.XMLReader;\nimport org.xml.sax.helpers.DefaultHandler;\n\npublic class bc extends DefaultHandler {\n   private boolean a = false;\n   private StringBuilder b = new StringBuilder();\n   private Map<String, ay> c = new TreeMap();\n   private List<az> d = new ArrayList();\n   public static int e;\n\n   public void characters(char[] var1, int var2, int var3) throws SAXException {\n      this.a = true;\n      this.b.append(var1, var2, var3);\n   }\n\n   public void endDocument() throws SAXException {\n   }\n\n   public void endElement(String var1, String var2, String var3) throws SAXException {\n      int var7 = e;\n\n      bc var10000;\n      label63: {\n         label67: {\n            try {\n               var10000 = this;\n               if (var7 != 0) {\n                  break label63;\n               }\n\n               if (!this.a) {\n                  break label67;\n               }\n            } catch (SAXException var10) {\n               throw var10;\n            }\n\n            String var4 = this.b.toString();\n            Iterator var5 = this.d.iterator();\n\n            label55: {\n               while(var5.hasNext()) {\n                  az var6 = (az)var5.next();\n\n                  try {\n                     var6.a(var4);\n                     if (var7 != 0) {\n                        break label55;\n                     }\n\n                     if (var7 != 0) {\n                        break;\n                     }\n                  } catch (SAXException var9) {\n                     throw var9;\n                  }\n               }\n\n               this.b = new StringBuilder();\n            }\n\n            this.a = false;\n         }\n\n         var10000 = this;\n      }\n\n      Iterator var11 = var10000.d.iterator();\n\n      while(var11.hasNext()) {\n         az var12 = (az)var11.next();\n\n         try {\n            if (var12.b(var1, var3)) {\n               var11.remove();\n            }\n         } catch (SAXException var8) {\n            throw var8;\n         }\n\n         if (var7 != 0) {\n            break;\n         }\n      }\n\n   }\n\n   public void processingInstruction(String var1, String var2) throws SAXException {\n      Iterator var3 = this.d.iterator();\n\n      while(var3.hasNext()) {\n         az var4 = (az)var3.next();\n         var4.a(var1, var2);\n      }\n\n   }\n\n   public void startElement(String var1, String var2, String var3, Attributes var4) throws SAXException {\n      Iterator var5 = this.d.iterator();\n\n      while(var5.hasNext()) {\n         az var6 = (az)var5.next();\n         var6.a(var1, var3, var4);\n      }\n\n      try {\n         ay var9 = (ay)this.c.get(var3);\n\n         try {\n            if (var9 != null) {\n               this.d.add(new az(var9, var1, var3, var4));\n            }\n\n         } catch (ParserConfigurationException var7) {\n            throw var7;\n         }\n      } catch (ParserConfigurationException var8) {\n         throw new SAXException(var8);\n      }\n   }\n\n   public void a(String var1, ay var2) {\n      this.c.put(var1, var2);\n   }\n\n   public static a0 a(Node var0) {\n      return new a1(var0);\n   }\n\n   public void a(InputStream var1) throws ParserConfigurationException, SAXException, IOException {\n      try {\n         SAXParserFactory var2 = SAXParserFactory.newInstance();\n         SAXParser var3 = var2.newSAXParser();\n         XMLReader var4 = var3.getXMLReader();\n         var4.setEntityResolver(new a2(this));\n         var4.setContentHandler(this);\n         var4.parse(new InputSource(var1));\n      } catch (a_ var8) {\n      } finally {\n         var1.close();\n      }\n\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/bd.java",
    "content": "import java.io.PrintWriter;\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.util.Hashtable;\nimport java.util.Vector;\n\npublic class bd {\n   private static String a;\n   private static String j;\n   private static MessageDigest b;\n   private static Hashtable c;\n   private static Hashtable d;\n   private static final boolean e = false;\n   private static String f;\n   private static Hashtable g;\n   private static Hashtable h;\n   private static final String x = \"\";\n   private static PrintWriter writer;\n   private static final String[] i;\n\n   private static void a(Hashtable var0, MessageDigest var1) {\n      var0.put(new BigInteger(i[46], 36), \"\\uffc0\");\n      var0.put(new BigInteger(i[17], 36), \"ﾏ\");\n      var0.put(new BigInteger(i[96], 36), \"ﾣ\");\n      var0.put(new BigInteger(i[94], 36), \"ￍ\");\n      var0.put(new BigInteger(i[24], 36), \"\\t\");\n      var0.put(new BigInteger(i[82], 36), i[60]);\n      var0.put(new BigInteger(i[64], 36), \"\\uffbf\");\n      var0.put(new BigInteger(i[36], 36), \"ￏ\");\n      var0.put(new BigInteger(i[56], 36), i[10]);\n      var0.put(new BigInteger(i[30], 36), i[70]);\n      var0.put(new BigInteger(i[75], 36), i[23]);\n      var0.put(new BigInteger(i[47], 36), i[5]);\n      var0.put(new BigInteger(i[19], 36), \"ﾖ\");\n      var0.put(new BigInteger(i[2], 36), i[71]);\n      var0.put(new BigInteger(i[50], 36), \"ￊ\");\n      var0.put(new BigInteger(i[76], 36), \"O\");\n      var0.put(new BigInteger(i[40], 36), i[15]);\n      var0.put(new BigInteger(i[80], 36), i[33]);\n      var0.put(new BigInteger(i[95], 36), i[38]);\n      var0.put(new BigInteger(i[89], 36), i[69]);\n      var0.put(new BigInteger(i[43], 36), \",\");\n      var0.put(new BigInteger(i[93], 36), i[77]);\n      var0.put(new BigInteger(i[62], 36), \"ﾊ\");\n      var0.put(new BigInteger(i[22], 36), i[66]);\n      var0.put(new BigInteger(i[14], 36), i[34]);\n      var0.put(new BigInteger(i[29], 36), i[49]);\n      var0.put(new BigInteger(i[88], 36), i[25]);\n      var0.put(new BigInteger(i[42], 36), i[39]);\n      var0.put(new BigInteger(i[45], 36), i[11]);\n      var0.put(new BigInteger(i[102], 36), i[73]);\n      var0.put(new BigInteger(i[86], 36), i[0]);\n      var0.put(new BigInteger(i[41], 36), i[100]);\n      var0.put(new BigInteger(i[74], 36), i[51]);\n      var0.put(new BigInteger(i[103], 36), i[65]);\n      var0.put(new BigInteger(i[97], 36), i[85]);\n      var0.put(new BigInteger(i[92], 36), i[104]);\n      var0.put(new BigInteger(i[13], 36), i[20]);\n      var0.put(new BigInteger(i[78], 36), i[90]);\n      var0.put(new BigInteger(i[6], 36), \">\");\n      var0.put(new BigInteger(i[32], 36), \"\\uffdd\");\n      var0.put(new BigInteger(i[63], 36), \"ￛ\");\n      var0.put(new BigInteger(i[53], 36), \"\\uffd0\");\n      var0.put(new BigInteger(i[7], 36), i[12]);\n      var0.put(new BigInteger(i[54], 36), i[67]);\n      var0.put(new BigInteger(i[4], 36), \"ﾗ\");\n      var0.put(new BigInteger(i[3], 36), i[57]);\n      var0.put(new BigInteger(i[79], 36), \"\\uffd0\");\n      var0.put(new BigInteger(i[16], 36), \"A\");\n      var0.put(new BigInteger(i[44], 36), i[18]);\n      var0.put(new BigInteger(i[31], 36), i[58]);\n      var0.put(new BigInteger(i[21], 36), i[55]);\n      var0.put(new BigInteger(i[35], 36), \"-\");\n      var0.put(new BigInteger(i[9], 36), i[87]);\n      var0.put(new BigInteger(i[61], 36), i[81]);\n      var0.put(new BigInteger(i[72], 36), i[48]);\n      var0.put(new BigInteger(i[68], 36), \"\\u0010\");\n      var0.put(new BigInteger(i[26], 36), \"ﾜ\");\n      var0.put(new BigInteger(i[91], 36), i[1]);\n      var0.put(new BigInteger(i[28], 36), i[101]);\n      var0.put(new BigInteger(i[37], 36), \"u\");\n      var0.put(new BigInteger(i[99], 36), i[52]);\n      var0.put(new BigInteger(i[105], 36), i[59]);\n      var0.put(new BigInteger(i[27], 36), \"ﾰ\");\n      var0.put(new BigInteger(i[8], 36), i[98]);\n      var0.put(new BigInteger(i[83], 36), \"\\uffd0\");\n      var0.put(new BigInteger(i[84], 36), \"\\uffde\");\n   }\n\n   private static void b(Hashtable var0, MessageDigest var1) {\n   }\n\n   private static void c(Hashtable var0, MessageDigest var1) {\n   }\n\n   private static void d(Hashtable var0, MessageDigest var1) {\n   }\n\n   private static void e(Hashtable var0, MessageDigest var1) {\n   }\n\n   private static void f(Hashtable var0, MessageDigest var1) {\n   }\n\n   private static void g(Hashtable var0, MessageDigest var1) {\n   }\n\n   private static void h(Hashtable var0, MessageDigest var1) {\n   }\n\n   private static void i(Hashtable var0, MessageDigest var1) {\n   }\n\n   private static void j(Hashtable var0, MessageDigest var1) {\n   }\n\n   public static String a(String var0) {\n      if (b == null) {\n         return var0;\n      } else {\n         try {\n            int var1 = var0.lastIndexOf(\"[\") + 1;\n            String var2 = var0.substring(var1);\n            if (var1 > 0 && var2.length() == 1) {\n               return var0;\n            } else {\n               boolean var3 = false;\n               if (var2.charAt(0) == 'L' && var2.charAt(var2.length() - 1) == ';') {\n                  var3 = true;\n                  var2 = var2.substring(1, var2.length() - 1);\n               }\n\n               boolean var4 = var2.indexOf(46) > -1;\n               if (var4) {\n                  var2 = var2.replace('.', '/');\n               }\n\n               var2 = var2 + f;\n               String var5 = b(var2);\n               if (var5 == null) {\n                  return var0;\n               } else {\n                  if (var4) {\n                     var5 = var5.replace('/', '.');\n                  }\n\n                  StringBuffer var6 = new StringBuffer();\n\n                  for(int var7 = 0; var7 < var1; ++var7) {\n                     var6.append('[');\n                  }\n\n                  if (var3) {\n                     var6.append('L');\n                  }\n\n                  var6.append(var5);\n                  if (var3) {\n                     var6.append(';');\n                  }\n\n                  return var6.toString();\n               }\n            }\n         } catch (Throwable var8) {\n            return var0;\n         }\n      }\n   }\n\n   public static String b(String var0, Class var1, Class[] var2) {\n      if (b != null && var1 != null) {\n         try {\n            String var3 = var1.getName();\n            String var4 = var3.replace('.', '/');\n            StringBuffer var5 = new StringBuffer();\n            var5.append(f);\n            var5.append(var0);\n            var5.append(f);\n            if (var2 != null && var2.length > 0) {\n               for(int var6 = 0; var6 < var2.length; ++var6) {\n                  Class var7 = var2[var6];\n                  var5.append(d.containsKey(var7) ? (String)d.get(var7) : var7.getName().replace('.', '/'));\n                  var5.append(f);\n               }\n            }\n\n            String var10 = var5.toString();\n            String var11 = var4 + var10;\n            String var8 = b(var11);\n            if (var8 != null) {\n               return var8;\n            } else {\n               var8 = a(var1, var10);\n               return var8 != null ? var8 : var0;\n            }\n         } catch (Throwable var9) {\n            return var0;\n         }\n      } else {\n         return var0;\n      }\n   }\n\n   public static String c(Class var0, String var1) {\n      if (b != null && var0 != null) {\n         try {\n            String var2 = var0.getName();\n            String var3 = var2.replace('.', '/');\n            StringBuffer var4 = new StringBuffer();\n            var4.append(f);\n            var4.append(var1);\n            String var5 = var4.toString();\n            String var6 = var3 + var5;\n            String var7 = b(var6);\n            if (var7 != null) {\n               return var7;\n            } else {\n               var7 = a(var0, var5);\n               return var7 != null ? var7 : var1;\n            }\n         } catch (Throwable var8) {\n            return var1;\n         }\n      } else {\n         return var1;\n      }\n   }\n\n   private static String b(String var0) {\n      String var1 = (String)g.get(var0);\n      if (var1 == null && var1 != \"\") {\n         b.reset();\n\n         try {\n            b.update(var0.getBytes(j));\n         } catch (UnsupportedEncodingException var4) {\n         }\n\n         byte[] var2 = b.digest();\n         BigInteger var3 = new BigInteger(var2);\n         var1 = (String)c.get(var3);\n         if (var1 != null) {\n            var1 = a(var0, var1);\n            g.put(var0, var1);\n         } else {\n            g.put(var0, \"\");\n         }\n      }\n\n      return var1 == \"\" ? null : var1;\n   }\n\n   private static String a(String var0, String var1) {\n      b.reset();\n      byte[] var2 = null;\n\n      try {\n         var2 = (var0 + a).getBytes(j);\n      } catch (UnsupportedEncodingException var9) {\n      }\n\n      b.update(var2);\n      byte[] var3 = b.digest();\n      char[] var4 = var1.toCharArray();\n      StringBuffer var5 = new StringBuffer(var4.length);\n\n      for(int var6 = 0; var6 < var4.length; ++var6) {\n         char var7 = var4[var6];\n         byte var8;\n         if (var6 < var3.length - 1) {\n            var8 = var3[var6];\n         } else {\n            var8 = var3[var6 % var3.length];\n         }\n\n         var5.append((char)(var7 ^ (char)var8));\n      }\n\n      String var10 = var5.toString();\n      return var10;\n   }\n\n   private static String a(Class var0, String var1) {\n      Vector var2 = b(var0);\n      int var3 = var2.size();\n\n      for(int var4 = 0; var4 < var3; ++var4) {\n         String var5 = (String)var2.elementAt(var4);\n         String var6 = var5 + var1;\n         String var7 = b(var6);\n         if (var7 != null) {\n            return var7;\n         }\n      }\n\n      return null;\n   }\n\n   private static String a(Class var0) {\n      return d.containsKey(var0) ? (String)d.get(var0) : var0.getName().replace('.', '/');\n   }\n\n   private static Vector b(Class var0) {\n      String var1 = var0.getName();\n      Vector var2 = (Vector)h.get(var1);\n      if (var2 != null) {\n         return var2;\n      } else {\n         Vector var3 = new Vector();\n         Hashtable var4 = new Hashtable();\n         b(var0, var3, var4);\n         h.put(var1, var3);\n         return var3;\n      }\n   }\n\n   private static void b(Class var0, Vector var1, Hashtable var2) {\n      Class var3 = var0.getSuperclass();\n      if (var3 != null && !var2.containsKey(var3)) {\n         var1.addElement(var3.getName().replace('.', '/'));\n         var2.put(var3, var3);\n         b(var3, var1, var2);\n      }\n\n      Class[] var4 = var0.getInterfaces();\n\n      for(int var5 = 0; var5 < var4.length; ++var5) {\n         Class var6 = var4[var5];\n         if (!var2.containsKey(var6)) {\n            var1.addElement(var6.getName().replace('.', '/'));\n            var2.put(var6, var6);\n            b(var6, var1, var2);\n         }\n      }\n\n   }\n\n   private static String c(Class var0) {\n      return var0.getName().replace('.', '/');\n   }\n\n   static {\n      String[] var10000 = new String[106];\n      char[] var10003 = \"ￖ\\\"\".toCharArray();\n      int var10005 = var10003.length;\n      char[] var10004 = var10003;\n      int var12 = var10005;\n\n      int var2;\n      char var10007;\n      byte var10008;\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"aq\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[1] = (new String(var10004)).intern();\n      var10003 = \"\\u0005c(\\u001f_X~+G\\u0007\\u001000\\u0007\\u0007\\\\k%B^K34\\u001f@Oa7\\u001dY\\u001ak.\\u001fE\\u001dj5N^@?5\\u000eBJ6(O\\u0006\\u001dq5\\u0018\\u0004\\u001efv\\u001eEJa0G\\tE~#\\u0002\\u0004^k;\\u0018\\tIn%@_N08\\u0011AOe.\\u0014T]09\\u0001I\\u001ej&\\u001a@\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[2] = (new String(var10004)).intern();\n      var10003 = \"\\u000537\\u000e\\\\[b&\\u0006Z_cr\\u001f\\u0006B?-\\rK\\u001a0p\\u0011_AwpFEFvr\\u0007D\\u0011w3\\u0014RPm'\\u0018Y\\u0011vq\\u0015[Ba'AH\\u001a?%\\u0001\\u0004Ca3\\r\\u0003O?;D[Gh6CV\\u001cw3\\u0010\\u0000YwxC@L}9\\u0005C]}4\\u0015\\u0005^38\\u0010\\\\\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[3] = (new String(var10004)).intern();\n      var10003 = \"\\u000533\\u000f\\u0004Y0q\\u0015WZe7\\u000fKYb6NDL\\u007f7\\u001bPId,NYD0s\\u0010@Djw\\u0016UM3+\\u0019\\b\\u001b?v@UL`t\\u0019^\\u001a?&FE\\u001db1\\u0016V\\u001f10\\u000f\\u0000Nm.ER]q8@R\\u001e37A\\u0005\\u001dfy\\u0000@\\u0018b)\\u000fEJa \\u001c\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[4] = (new String(var10004)).intern();\n      var10003 = \"￥￡\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[5] = (new String(var10004)).intern();\n      var10003 = \"\\u00053w\\u0015X\\u001e4#\\rSK`2\\u0003]Cr&\\u001fI_b)\\u001eDPbxDTO1;OEZq$\\u001e\\u0006Ojv@T\\u0011mx\\u001d\\u0000\\u001cp.\\u0007\\u0005Ed&\\u0006F\\u001enp\\r\\t\\u001c?&\\u0015XJ?'OT^ev\\u0015\\u0002F60\\u0010RBk&\\u0007F_32\\u0005W\\u001fb8F@\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[6] = (new String(var10004)).intern();\n      var10003 = \"\\u001ev5\\u0006P\\u001b>&\\u0007H@7-GCI>)\\u0007S\\u001ej;\\u0010UMm AYO2#\\u000fZ\\u001e7s\\u001cS\\u0010oq\\u001fVFu\\\"DWG?'OZ\\u0011`&\\u0013\\t]fw\\u001aH\\u0019o$DWG~&@^P7rDK\\u0018q$FPL}2A\\u0001No \\u0018PLjv\\u000e\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[7] = (new String(var10004)).intern();\n      var10003 = \"\\u00054r\\u0007YI4,\\u0018RZ~(\\u0001K\\u00187x\\u001aC]e8\\u000e\\u0004\\u00117/FRMmuNK\\u0010k'\\u001cP\\u0019fp\\u001ePK5)@\\t_os\\u001f\\u0003J~/@P\\u001cm7\\u001fG\\u0010`+\\u0015\\u0005\\u0010tx\\u0011IPk/\\u001f\\u0004@isC\\t\\u001c0rCY\\u001auqC\\u0000\\u0010n7\\u001aT\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[8] = (new String(var10004)).intern();\n      var10003 = \"L3t\\u0019ZB30\\u0014\\bNs6\\u0010\\u0002Z1$\\u001aVI09\\u000eU]30F\\u0004\\\\qvN\\\\_o9BK\\u001f\\u007fw\\u0006DFb-N\\u0002[5'B[[00\\rZNb O\\u0006\\u001ew2\\u0002[Ltp\\u0013\\u0003G~qO_Qe)\\u001eSLh0\\u001a\\\\\\u001bs9\\u0013B\\u0010ds\\u0012\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[9] = (new String(var10004)).intern();\n      var10003 = \"￡ﾚ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[10] = (new String(var10004)).intern();\n      var10003 = \"\\ufff0\\u0007\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[11] = (new String(var10004)).intern();\n      var10003 = \"ﾯ>\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[12] = (new String(var10004)).intern();\n      var10003 = \"\\u00052+\\u0019\\u0000Jrp\\u001a]^k6\\u0010CJa8\\u0015DXbyFY\\u0018bx\\u001a\\u0007O0v\\u0005CD0y\\u0014[Np1\\u0004XKm8\\u0012\\u0007Ri2\\u0018\\u0000Rax\\u001bH\\u0010`#\\u0003\\u0006Pp6\\u001e\\u0001Aa%DYG0#\\u0001KJmy\\u0002WX6p\\u0018E\\u0010`5\\u001c\\u0002C}r\\u001b\\u0002\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[13] = (new String(var10004)).intern();\n      var10003 = \"\\u0005dv\\u0000GGl(\\u001aE\\u00192-\\u0014U\\u0011t8\\u0013\\b\\u0019c-\\u001e]Eq$\\u000fV\\u001av/CX\\u001ev'\\u001e\\\\Q}3\\u0012ZCmv\\u0002X\\u0019b6\\u0018RFc)\\u0007\\u0007\\u001c`7\\u0003XKiy\\u0006BYo0\\u0016\\u0004@c4\\u0010\\u0001BhrE\\u0004[i\\\"\\u0012C[au\\u0000SPsw\\u001aW\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[14] = (new String(var10004)).intern();\n      var10003 = \"\\uffc1\\ufff4\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[15] = (new String(var10004)).intern();\n      var10003 = \"It#\\u001c\\tOa1\\u001b[Km4\\u0001\\u0004Eq6\\u0003@\\u001bi.\\u0005C_n;\\u0015[Dfr\\u0001\\u0005Ri*\\u0013\\u0004B0*\\u0002]Js,\\u000fFP25A_D>;\\u0012SDju\\u0010^\\u0011e7GY_k-\\u0001Z\\u001d>9\\rI^?4\\u0004DK}4\\u001e_Z6#\\u0012EX0u\\u000e\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[16] = (new String(var10004)).intern();\n      var10003 = \"\\u00051+OT_d$\\u0016F\\u0019n*\\u0006\\u0005\\u001ae(\\u000f_J~wG\\u0000\\u001ee1@R]>u\\u0019\\u0004RpwNUJ2t\\u0007\\u0001Ol,\\u0004RAp4\\u0006GZ79\\u0011F\\u0011n7\\u0001\\u0005^cvB\\u0007Ou$\\u0002EQ}q\\u0007ZOw&\\u0007ICo3\\u0014[\\u001ft,EB\\u001bnx\\u0002S\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[17] = (new String(var10004)).intern();\n      var10003 = \"ￃﾾ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[18] = (new String(var10004)).intern();\n      var10003 = \"Ah,\\r\\u0006Qq+\\u0018]Gn9\\u001eG\\u00111p\\u0004XBr0O\\u0001Fj+CK\\\\rsCU\\u001f4p\\u001cSD7r\\u0005KL2vDYYi/\\u0012\\u0002B0s\\u0005VYuv\\u0000VZ2%\\u0007TI3'\\u0006V\\u001b31\\u001aW\\u00183+\\u0011W\\u0011v;\\u0002\\u0000Jc-\\u0015\\u0007\\u001dn3B\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[19] = (new String(var10004)).intern();\n      var10003 = \"�\\u001e\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[20] = (new String(var10004)).intern();\n      var10003 = \"\\u0005mq\\u0014SOt-\\u001a\\u0003[~p\\u001dPD}*\\u001e\\u0001Fl'\\u001aA\\u001b58\\u0006KOrv\\u000fUF\\u007f'\\u0010_Mj,\\u001d\\t\\u001bs-\\u0006\\u0004\\u001ej3\\u0005E_v2\\u0002\\b\\u0011\\u007f \\u0010@Etw\\u0007\\\\Oet\\u001c\\u0003\\u001dl \\u0002CA30\\u001a\\u0002\\\\u(EUEn C\\u0004\\u001f?;O\\u0000\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[21] = (new String(var10004)).intern();\n      var10003 = \"C2/\\u0013\\u0007Dq1\\u0019WY09FWYo7NE\\\\b3\\u001dEXlx\\u0003W\\u001frw\\u001a\\u0005Yn/\\u0015ZJm.@\\u0007\\u001fr4E\\t\\u001e4(\\u0013\\t\\u0010k2\\u001fVM0%\\u0005PF0(@F\\u001f12\\u001bPKp9\\u001eHYu\\\"O\\u0002\\u001c~;@\\u0006Rk AW]uy\\u0006\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[22] = (new String(var10004)).intern();\n      var10003 = \"Jￚ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[23] = (new String(var10004)).intern();\n      var10003 = \"\\u001eq%\\u001b\\u0004F06N\\u0005\\\\?5FT_n3\\u0007\\u0003@a%NZ\\u001d\\u007f1\\u0002\\u0007IlxN\\u0000@>pBILj+\\u0018GGu;@R^pp\\u0000SC2*\\u0004\\u0003\\\\3#\\u0007WAa9\\r\\u0007\\u0018m/\\u0006\\u0000A?+DF\\u001bn)\\u000fW[74\\u001bR\\u0011qt\\u0015W]\\u007fx\\u001e\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[24] = (new String(var10004)).intern();\n      var10003 = \"ﾫﾾ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[25] = (new String(var10004)).intern();\n      var10003 = \"\\u00050%\\u001aU\\u001f`+EDFm\\\"\\u001cDXc,\\u001aTN\\u007f#\\u0004\\u0001A~ \\u0014YAq9\\u001dC\\u00105r\\u0015SNb-E\\t]`)\\u0002@^>;\\u0006XZkr\\u0005\\\\^0\\\"E^_6)\\u0013\\u0004X>$\\u0001@Eu/\\u0003A_r$DIJ\\u007f+\\u0003\\u0001AhrE\\u0000E5w\\u0014T\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[26] = (new String(var10004)).intern();\n      var10003 = \"\\u000533O\\bBa%O[\\u0011q'\\u0015Z@r(D\\t\\u001b61\\u0013PA?*\\u0014_\\\\d5\\u0015HAqu\\rFE\\u007f4\\u001cS^24\\u0007\\u0003\\u00112q\\u0014@\\u0011l(\\u001b\\u0007\\u001cs5\\u0001\\bI?$BINs\\\"\\u0010SXv+NZ]d/\\u0019EFr$\\u000eEKws\\u001bB\\u001c4 \\u0014A\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[27] = (new String(var10004)).intern();\n      var10003 = \"No*\\u0006GG4q\\u001b\\t\\u00116t\\u0019RD}p\\u0019FO>'GFQ4/\\u001bCP`)\\u0002[Dk7B\\u0003I1uCZD>9\\u0003K\\u0019i%G@O`.\\u001cY\\u0011n-\\u0004V\\u001dpu\\u0018WY?+\\u0005GG~7\\u0002A]c*OFCm,\\u0012\\u0006\\u001c7y\\u0006\\u0003N40\\u0005\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[28] = (new String(var10004)).intern();\n      var10003 = \"\\u001abp\\u0019X\\u0018a$OT\\u001bq)\\u0006\\u0001Art\\u0001FX6 \\u0001BX~p\\u0014PDd;\\u0001XPp5C@Bfy\\u000fW\\u0018m)\\u0000\\u0002E`-\\u0018WNmx\\u0014\\\\Gu+\\u0002S\\u001a`&\\u0014\\u0005\\u001bbw\\u0010\\u0004\\u0010a*\\u001f\\tN5#@ILe(\\u0014BOt.\\u0018IRi(\\u001f\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[29] = (new String(var10004)).intern();\n      var10003 = \"\\u0005?uCSPk&\\u0003V\\u001fbr\\u0019R[nt\\u0010CYv%AIMtw\\u0005H\\u00101)\\u0012\\u0005\\u0019tq\\u0000YEp$\\u0004\\b\\u0019jx\\u0012I\\u001aos\\u0010\\u0006\\u001e3x\\u0012PDq'FZCt2\\u0019CZi&DX_? \\u0014S[p9@\\tDaw\\u0003AIa-\\u001eYFqu\\u0010\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[30] = (new String(var10004)).intern();\n      var10003 = \"\\u0005j4N@Ct*GI\\u0010c-\\u0014UCat\\u001eI_5;\\u0016^\\u001ee2OS\\u001eq \\u0002XB3,\\u0019T\\u001cwt\\u0002]B7s\\u0015\\tM?%\\u0014[A0.\\u0005\\u0003Aq%\\u001eXQ22@CPa1\\u0010\\t\\u001cl'\\u001d\\bNu(\\u0014SOh7G\\u0003X0/\\u0003FMh7\\u0007\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[31] = (new String(var10004)).intern();\n      var10003 = \"\\u0005`7\\u0018ZYo A\\u0005X2'\\u0007^_s8GR\\\\>(D@]tr@\\u0007@>0\\u0018DK2+\\u000fBC5q\\u0007\\u0007\\u001epq\\u001c\\\\P30\\u0006IEp#\\u0001G\\u00112-\\r\\u0001\\u001ajw\\u001dIL1wNIE~3BXZ?#O@Os0EA\\u001em(\\u000f\\u0005\\u00105tN\\u0004\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[32] = (new String(var10004)).intern();\n      var10003 = \"ﾖx\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[33] = (new String(var10004)).intern();\n      var10003 = \"5ﾸ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[34] = (new String(var10004)).intern();\n      var10003 = \"K3sDG\\u001av(\\u0015\\\\Mi*\\u0014\\u0000\\\\0,\\u001dF^s(E\\u0005Qp.DK\\u001d}5\\u000e\\u0007Rey\\u0006KGe%\\u001cHM03CARr(\\u0012S@f/\\u000fK\\u001106@\\u0003_i%\\u000e\\\\Rt6\\u0004U\\u001ab,\\u0019\\\\Xi$@\\t\\u00115-\\u0016\\t]31N]\\u001d\\u007f9\\r\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[35] = (new String(var10004)).intern();\n      var10003 = \"\\u0005?t\\u0014\\u0004\\u001fl+\\u000f@Gk*\\u0005EKa0\\u0013T\\u001dr*G\\bXes\\u001e\\u0000P}-C^@u6\\u001cH\\u00116&NG\\u0019v+AV\\u001c}6\\u000f[Cq\\\"\\u001f\\t\\\\68\\u0013\\t^\\u007f%\\u0015\\u0006Ge'\\u0015RErp\\u0005ZZ7+\\u001cFJm%\\u001d\\u0003Q67ADIaxO^\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[36] = (new String(var10004)).intern();\n      var10003 = \"\\u001a06\\u001cFE0.F[Zq%GHCtq\\u0007\\u0002\\u0011~+\\u0006TDf*\\u0010\\u0001C>#B\\u0000^ps\\u000fKMc.\\u0011DO2r\\u0019A_ap\\u0011AMw0G\\u0000Am5A@A>1\\u0016@Ot,\\u0002]Lu8\\u000e\\u0000Rv-\\u0019P\\u0019h4\\u000e^\\\\4'BVZ\\u007fu\\u0001\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[37] = (new String(var10004)).intern();\n      var10003 = \"k￣\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[38] = (new String(var10004)).intern();\n      var10003 = \"\\uffc1ﾡ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[39] = (new String(var10004)).intern();\n      var10003 = \"[79\\u0019R\\u001fd;E\\u0004F\\u007f @YX06\\rWC0x\\u001bW_t\\\"\\u0005X\\u0011o8\\u001fFX6uA\\u0004]v\\\"\\u001e\\u0004Me3\\u001eAM~/DDB41\\u0004\\u0005@c0EXXh.\\u0012B]uw\\u0002AA44\\u000e_\\u0018e(\\u0003EF3p\\u0006BB41\\u0015K\\u001048\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[40] = (new String(var10004)).intern();\n      var10003 = \"\\u0005>p\\u001fPA7+\\u001fK\\u001c0*\\u0010\\u0000\\u0011k \\u0014H\\\\cxNWNs*\\u0000C\\u001c6.@]]m,OBZv0\\u0007\\u0002Fsu\\u001fWF3rOXE?x\\u000e\\u0005Pj,F\\\\C0p\\u0002[O42\\u001a\\u0001Qm0BU\\u001f`8\\u0018\\\\E}1E\\u0000\\u0010lv\\r\\t\\u00100;\\u0015\\u0004\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[41] = (new String(var10004)).intern();\n      var10003 = \"\\u0005o3\\u0018FA37\\u0012AM6w\\u001cXCk+\\u0013\\u0000^my\\u0015UOnw\\u0003SYj1\\u0010AZ5'\\u0014X\\u001ap5\\u001dFEmr\\u0003]@qvF\\u0002Z~xOXR72E\\u0001\\\\v/AH\\u001bv$CC@v+\\u0015BB08\\u0007\\u0004Qi;\\u0013W\\u001ddw\\u0000S\\u0010j;\\u0000B\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[42] = (new String(var10004)).intern();\n      var10003 = \"\\u0005>pB\\u0004Q1w\\u0019WI?\\\"\\u0012\\u0005On+\\u0007IJf)\\u0013FYe,\\u0014\\u0003Ad%\\u0013KL2'\\u001f@C0#\\u0012\\u0000[sy\\u0018W[t'\\u0012ZR0r\\u0011A\\u0018ew\\u0011AGp)\\u001aTL}vC__up\\u001f\\u0001M>\\\"A\\u0004P},GKBu3D\\t\\u001928\\u0011\\u0005\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[43] = (new String(var10004)).intern();\n      var10003 = \"Mep\\u0005BNh)FT\\u0018d6NIKa4\\u0003W\\u001d`(\\u0004[Pb7\\u001dWLd*\\u0004FKo.\\u001aZOh1\\u0013[Zt*FK_m;\\u001e\\u0000\\u001fe6\\u0003\\u0000C\\u007f&\\u000eUG2p\\u0016UR3vO\\u0006Dd3\\u0005[Yl$DC^~t\\u0003\\bQb2\\u000fCD?5\\u0007\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[44] = (new String(var10004)).intern();\n      var10003 = \"\\u001c2,\\u0003@EqqOP\\u0010mt\\u0003WQ}8\\u0014\\u0001L4*\\u0004AK50\\u0012XI}w\\u0015\\t@0)FH^q\\\"\\u0018V\\u0019r#N\\u0003R7q\\u000fV]ft\\u0004H\\u001d?$\\u0014\\\\J~/\\u0005\\u0002G0v\\u0006RIw'\\u001d_\\u001c7.\\u001e\\u0003Y>9\\u001aTYh7\\rC_m8\\u001d\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[45] = (new String(var10004)).intern();\n      var10003 = \"\\u000506DG\\u001dk\\\"\\u000f\\u0000Fl \\u0011[\\u001d>$N^@ht\\u0000]Gv\\\"\\u0006[Ev;\\u0013HMr\\\"\\u0003\\bA71\\u0012_Ci*\\u001fE\\u001e}.\\u001dVC6r\\u0007K\\u0011?\\\"G\\u0007I?tA_\\u001awq\\u0013RRtu\\u0001\\u0002D02DFL>3\\u0015F\\u001d3t\\u0014P\\u001fh9\\u001f\\\\\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[46] = (new String(var10004)).intern();\n      var10003 = \"Je8\\u0000^\\u0018fp\\u0000[F5uAT\\u001auqN^Il3OKDm6\\u001bEZu+\\u001e\\tB7v\\u0010\\bEuw\\u0010\\u0006\\u001cmw\\u000e\\u0006Kru\\u001c\\u0001N\\u007f(@W\\\\6.\\u0011G\\u0018hv\\u001aCJw(\\u000eT@f'\\u0007WOu4C\\u0003Njq\\u001d\\t\\\\h;\\u0004W\\u001da\\\"A\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[47] = (new String(var10004)).intern();\n      var10003 = \"?\\uffc8\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[48] = (new String(var10004)).intern();\n      var10003 = \"L￡\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[49] = (new String(var10004)).intern();\n      var10003 = \"Id)GI\\u001b5&\\u0006TY1y\\u000fPDh7\\u001a@R29\\u0016[Ae*\\u000fWL7t\\u0013T\\u001fp6E\\u0005\\u0019ayG\\u0006\\u001abq\\u0001]Kv.\\u001fTF5/\\u0003\\u0001Rb4\\u0011FDv\\\"ES^}q\\u0016TOr2\\u0019\\u0006\\\\v(\\u0002F]}(\\u001aFQ>+\\u001dXPl)\\u0004\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[50] = (new String(var10004)).intern();\n      var10003 = \"mc\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[51] = (new String(var10004)).intern();\n      var10003 = \"ￂ￥\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[52] = (new String(var10004)).intern();\n      var10003 = \"\\u0005o9\\u0005\\u0000_h4\\u0016RK1y\\u000e@_\\u007f0\\u0006\\u0007\\u0019n+OHN~*A\\u0004Ap/\\u001cUZ\\u007f.EICc4\\u0019IXa2\\u0000WL};\\u0019BCa0\\u0001\\u0006\\u001a\\u007fp\\u0010A\\\\?v\\u0001\\u0001Frw\\u0010I\\u001fmuF\\u0004\\u001d`%\\u0007G^j)\\u0003\\u0001E\\u007f+\\u0002EBi5N\\u0006\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[53] = (new String(var10004)).intern();\n      var10003 = \"@68\\u001cE_3(D\\u0001\\u0010k%\\u0002\\u0001\\u001erq\\u000eVA~0EHKw0\\u0018@Af9\\u000eFPep\\u0007_\\u0010h0\\u0005\\\\Inr\\u0014\\u0007^b0\\u0013\\\\\\u001c54\\u0006\\u0005\\u001e~%\\u001eU@nx\\u000e[\\u001de'\\u0013@]w#D\\u0005\\u0010jx\\u001fK\\u001bi;\\u0007AR24\\u0011\\u0006OvtD\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[54] = (new String(var10004)).intern();\n      var10003 = \"9-\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[55] = (new String(var10004)).intern();\n      var10003 = \"N77\\u0014\\u0003Na#E\\u0006FmuA\\\\\\u0011?v\\u0004DYu\\\"\\u0011KK>+\\u000e\\u0007\\\\iv\\u0015K\\u001fsp\\u0007[E07\\u0019POu.\\u0003EBvvF[\\u00113w\\u0005\\\\Lr#DTC\\u007f1\\u0007\\u0005Qv/\\u0014\\u0002Kr%\\u0014P\\u0019pv\\u0002FG60DS\\u001d`1E\\u0003\\u001a7q@\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[56] = (new String(var10004)).intern();\n      var10003 = \"\\u0017\\uffdf\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[57] = (new String(var10004)).intern();\n      var10003 = \"|ﾇ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[58] = (new String(var10004)).intern();\n      var10003 = \"\\b\\u0002\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[59] = (new String(var10004)).intern();\n      var10003 = \"ￍﾭ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[60] = (new String(var10004)).intern();\n      var10003 = \"A3%\\u0001^\\u001eo9\\u001aX^k0CDIt7\\u0006^C6p\\u0012KQks\\u000e_]r(D]Ko#\\u0014G_a8\\u0016CO7.AV]57\\u0015\\b\\u0018?;\\u000e_\\u00184rEI\\u001bh5\\u0005IL02\\u001aSMs(FYAkt\\u0010PMo&EB\\u001921\\u0000^C?(E\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[61] = (new String(var10004)).intern();\n      var10003 = \"\\u0005et\\u001d\\t\\u001a}%E\\u0005\\u001eoq\\rE\\u001fwp\\u0006_I3)\\u0010\\u0001N`1\\u0007U_4w\\u0004X\\u001b72\\u0007PA~v\\u0006EDwv\\u001fR@>7\\u001dXLv$\\u0003C\\u0019dx\\u0012\\u0007\\u001dw-\\u0015\\\\@1#\\u0002EQj2GWM\\u007f9\\u0006E@k7\\u0005\\u0004]l4\\u0019K\\u001ctw\\r\\u0001\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[62] = (new String(var10004)).intern();\n      var10003 = \"Lrx\\u0010W]o \\u0000@P~6\\u0011\\u0000[j3\\u001cCZ\\u007f%\\u0007FZt \\u001b\\u0005\\\\>(A\\t\\\\~tNH\\u001ft/\\u0015REp6\\u0002FMq9FIDu*\\u0015P\\u001cl#@[O?5\\u0003\\u0004\\\\e(\\u001dR\\u001dl8\\u0005UJs7\\u0018FFe;\\u001cDD?8\\u0006\\u0007Cl%\\u001f\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[63] = (new String(var10004)).intern();\n      var10003 = \"\\u0011k6GA[30\\r_B>y\\u001aEDtu\\u001aCK73\\u001fTEj/\\u0001]Fs4\\u001b^G~(CD\\u001cv*BD\\u001ab1\\u001bB\\u0019dx\\u0010\\bE3rCXLw;\\u0013H_k%\\u0000H\\u0011p;\\u0015\\u0005Jf(\\u0019IK6q\\u001bS\\u001bktFW^e\\\"\\u0007EXh&\\u001f\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[64] = (new String(var10004)).intern();\n      var10003 = \"￡￫\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[65] = (new String(var10004)).intern();\n      var10003 = \"\\u0014\\t\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[66] = (new String(var10004)).intern();\n      var10003 = \"],\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[67] = (new String(var10004)).intern();\n      var10003 = \"L09\\u0004PE76DEF\\u007fr\\u0019^\\u001a3+\\u001f\\u0000\\u001bm+\\u000fYOr(E\\bMay\\u000eZ_qs\\u001dSK3;A\\\\Ptt\\u0001RZi-N^Zt\\\"D[Kr6\\u0002\\u0003\\u001bm6G[^a#\\u000eSF\\u007f#\\u0012VN}2DSM32\\rC]1;\\u0007H\\u001dhu\\u000e\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[68] = (new String(var10004)).intern();\n      var10003 = \"ﾈﾯ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[69] = (new String(var10004)).intern();\n      var10003 = \"ﾧ(\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[70] = (new String(var10004)).intern();\n      var10003 = \"ﾺ3\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[71] = (new String(var10004)).intern();\n      var10003 = \"Ii&\\u0018\\u0001Ywp\\u0004CYdv\\u0006\\u0004^n1\\u000eU\\u001ajw\\u0000[\\\\tuNS\\u001app\\r\\u0003^l4@]G1vBGLs8\\u0014DN7u\\u001e_\\u0018tp\\u0000\\\\Oq-GS\\u0019n.\\u000f\\u0006Xk\\\"\\u0011Z\\u0018>w\\u001a[\\u0010hr@IE~5\\u0001\\u0007\\u001brw\\u001eBD75\\u0011\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[72] = (new String(var10004)).intern();\n      var10003 = \"ￏￅ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[73] = (new String(var10004)).intern();\n      var10003 = \"I75OZMr.\\u000eB^f-\\u0015\\u0004]m'\\u0015\\u0001Ei0\\u0018_E01EKQw-\\u0002\\tG`1\\u0007\\u0001\\u001ah/\\u0007^NauB_\\u0018d#FXBhv\\u0011CEsu\\u001aSE18@AF0#C\\u0002@70\\u0007[Al&\\u001cT\\u001fwv\\u0010Z_os\\u0018ZC}yE\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[74] = (new String(var10004)).intern();\n      var10003 = \"\\u0005b/N\\b\\u0018q.\\u001d\\u0002\\u0019e.C\\\\]r$OBP>2A\\b\\u001de#\\u001fT\\u001b59\\u0002G\\\\2p\\u001d\\bN`/\\u0016\\u0000Pp8\\u0018PBo0\\u000e\\u0006Xa'\\rE^d2\\u0013R\\u001cr&CC\\u001ckv\\u0004IAd6\\u001dCAl-\\u0015VNs8\\u001a\\u0007P`(\\u0015\\u0000Nj&\\u001b^\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[75] = (new String(var10004)).intern();\n      var10003 = \"Lk8\\u0001@\\u001eby\\r^Ao,\\u000fV\\u001ewq\\u0015EJ3&@\\u0003Yq\\\"FP\\u0010t\\\"\\u0018F^0qAVO~+\\u0000VY`s\\u000fI\\u001ei3OKMrs\\u0006^Lj5\\u0002\\\\\\\\f\\\"\\u000e\\u0000Kc0\\u0004\\u0001McxOKM?p\\u000e@^w&\\u0002Y\\u001bdyGGDcq\\u0001\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[76] = (new String(var10004)).intern();\n      var10003 = \"$\\u0007\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[77] = (new String(var10004)).intern();\n      var10003 = \"\\u00054,\\u001cD\\u0018m,\\u001dX\\u0018dy\\u0010[P7.\\u0012P[n8\\u000f@Gj4\\u0019\\u0002\\\\o0\\u0005]Y49\\u0016\\t\\u0018o&\\u001dR\\u0019q(\\u0011K\\u001er7GX\\u001bd/\\u001bT\\u00185x\\u0010PKl.F[D6/\\u0011\\u0003O>)\\u001e\\u0006\\u0011cyNZPc,\\u000e^\\u001fj*G_Xd \\u0011G\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[78] = (new String(var10004)).intern();\n      var10003 = \"\\u0019j;\\u000e\\u0006\\u001cq2\\u001e\\u0004Q?q\\u0000A\\u001eu FU\\u001csw\\u0007R\\u001ff4\\u0014\\u0004\\u0011r$\\u000eW\\u0010a)\\u0018C[\\u007fxG^B0%ED]59OSRkrA\\u0001Jh0\\u0012Z\\u0010u;\\u0019A\\u001cqs\\u0000\\\\\\u001f50\\u0014\\u0005\\\\v/\\u0000\\u0007\\u001ampC\\u0002Is&\\u0013\\u0005X>5\\u0016\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[79] = (new String(var10004)).intern();\n      var10003 = \"\\u000511\\u0019]\\u0019l)@X^ku\\u000eAQv(\\u0003]Ylp\\u001fSN?\\\"C\\t_b#DII`s\\u0014DMdp\\u0012R\\u001bh*\\u001b\\u0000G>\\\"\\u0018KGr-\\u001a_Gnv\\u0014W\\u001af*\\u001e\\u0000\\\\j/\\u0002V^04\\u0005IO~.@D\\u0010`.CH\\u00182/\\u0005XL53\\r\\t\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[80] = (new String(var10004)).intern();\n      var10003 = \"~x\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[81] = (new String(var10004)).intern();\n      var10003 = \"\\u001c1'\\u0019YQj/\\u0002A\\u001b2 D\\u0000Q`6\\u000fP\\u001bf/\\r\\u0004Cf3CZ^2.OVN5vAPMt \\u001fX\\u001e},\\u0015\\u0003\\u001en4\\u001e\\u0004El1\\u0014B\\u0010r(\\u0005FLsqCKAr.@\\tNl1\\u0013XDsx\\u0010Z\\\\71@\\tL0wFRY3v\\u0002\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[82] = (new String(var10004)).intern();\n      var10003 = \"\\u00053*\\u0012\\u0004Rn+\\u0018IM\\u007f*\\u0003\\u0001Lsu\\u0004T\\u001fnp\\u001b\\u0006N} \\u0016HPw4C\\bAaw\\u0018\\u0003Asp\\u0010\\u0006\\u0010}r\\u001f_C24\\u000fAN3qD\\u0000Nms\\u0006\\u0001Rv\\\"@@Z4(\\u001d\\u0002\\u00185;\\u0003\\u0000X7.A\\u0005L63\\u001a[Ke%\\u0010Y\\u001dd.GG\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[83] = (new String(var10004)).intern();\n      var10003 = \"\\u0005n6\\u000fR\\u00196q\\u0007^]a \\u0000_K4q\\u0015[Ybu\\u0015V\\u0019s\\\"\\u0010WM67\\u0005KPf0\\u001dVKuu\\u001cZI\\u007f7EB\\u001a7+\\u001bK^50OPGa+\\u0003SPd$GWJm%\\u0019VJdv\\u0019\\u0003@0r\\u001c@\\u00184vD^\\u0011k3\\u0012[@6#\\u0015\\u0006\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[84] = (new String(var10004)).intern();\n      var10003 = \"ￎ,\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[85] = (new String(var10004)).intern();\n      var10003 = \"\\u00055-\\u001bDMp6\\u0011B\\u001c4)B_@k0\\u0002\\u0000\\u001f`pDD\\u001am \\u0015\\u0002Qjp\\u0015XDe-\\u001dP\\u001ch2\\u0005\\u0006M18\\u0004][u&\\u0001I\\u001bf*\\u0000BBe6\\u000e^Zo\\\"\\u001bH^7\\\"\\u0004]La5A[Ks)\\u0016_J0r\\u0018@\\u0019?3\\u000eW@`5\\u0015[\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[86] = (new String(var10004)).intern();\n      var10003 = \" ﾡ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[87] = (new String(var10004)).intern();\n      var10003 = \"\\u0005a'\\u0013]Ed(\\u0006VBv/\\u0011GL61\\u0001K]s5D\\b\\u001ba8\\u000f\\\\D1s\\u0007WDr(BC\\u001er'\\u0012YOp(\\u0011GA1x\\u0015\\u0001[1.G\\\\Dj&\\rBNk*\\u0007YJb/\\u0006]Ir.\\u0013@Jq0\\u0015\\u0007\\u001avp\\u001bD^56G@_3)\\u0005\\t\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[88] = (new String(var10004)).intern();\n      var10003 = \"\\u0005d(\\u0002B\\u001ft3AI\\\\k0\\u0019PO?.\\u0019PRb.\\u0003TIep\\u001b\\\\GvrGCMwt\\u0016XAty\\u0006\\u0003\\u0011}s\\u0006[Ao-\\u001f\\b\\u001ahx\\u0006WLl2\\u0000@_w&\\u000f\\u0004Mu$\\u001f^E>/\\u0007Y\\u0018v9\\u0003R]f,\\u0004\\u0003@u)\\u0013\\bOm'DB\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[89] = (new String(var10004)).intern();\n      var10003 = \"ﾘﾒ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[90] = (new String(var10004)).intern();\n      var10003 = \"@`#\\u0018\\u0004Qa6\\u0018\\\\Btq\\u001a\\u0001\\u0010a3\\u0013I\\u001b1&E\\bAqy\\u0000]\\u0010h7\\u0005WG07\\u000f\\u0005Yj,\\u000eX\\u001c4-\\u0012H\\u001da5\\u0001AI74@R@~\\\"\\u0013TO>,\\u0000R\\u001cc-\\u001bIK5#\\u001e\\u0006\\u001ctq\\u0006YD6&O\\\\_\\u007f)\\u0004\\\\Aa7\\u001d\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[91] = (new String(var10004)).intern();\n      var10003 = \"\\u0005dr\\u0004\\u0006\\u001cjw\\u001fS\\u00113/\\u0007G\\u001834\\u001eIE53\\u0018P[>;\\u0016\\u0003Qe9\\u001a\\u0007Idy\\u001eYA?/E\\u0006Ci,A^Cur\\u000e@\\u0010w9\\u0019WI44\\rH\\u001afqG\\u0007]wwO\\u0003Gq0\\u0011SYs%\\u0019\\u0005^60\\u001c]_0s\\u001cD\\u001d2y\\u0011D\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[92] = (new String(var10004)).intern();\n      var10003 = \"\\u0005rx\\u0001\\t\\\\}1CP@w;\\u001bED4\\\"\\u0013_Z}y\\u001b\\u0003^6;\\u0007B\\u001e2%\\u0001SA\\u007fx\\u001e\\u0003\\u001b`q\\u001cU]l&\\u001aYGvpDD]l6\\u0004\\u0006A}0BEAop\\u0004GJ~9\\u0019_Dn9\\u001aPP56\\u0018C\\\\aw\\u0014KKv;\\u0012G\\u001edr\\u0003\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[93] = (new String(var10004)).intern();\n      var10003 = \"\\u00052(CV\\u001cmr\\u001c\\u0001Rt7\\u001cKE0(\\u0015[Qk5\\u0001BIb7A\\u0002Dwv\\u001fS\\u00106r\\r]Bc0\\u0001UR58EDQ3x\\u000ePD> A[Ks/OFJt2B^Ol E\\u0006F5y\\u0019\\u0005@kvEKC60\\rI\\u0010lu\\u001fDI71O\\u0007\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[94] = (new String(var10004)).intern();\n      var10003 = \"M?\\\"\\u0018\\b\\u001f4'\\u0013PZn4\\u0012]\\u001dm'\\u0011@\\u0011e'O\\u0001Oc.\\u001a\\u0000M>.G\\u0000\\\\3y\\u0013IPc3\\u001d\\u0007Me4@\\tBd+\\u0002H\\u0018f&D\\u0006A4x\\u0018T\\u001df4B\\u0006B7u\\u0019^\\u0019\\u007f3\\r\\tN?/\\u001d\\u0000_l\\\"\\u0014T\\u001ft'\\u0000V\\u0019p%\\u0000\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[95] = (new String(var10004)).intern();\n      var10003 = \"\\u0005`8\\u0001]Iw7\\u001c\\u0001\\u001ed'\\u001e]M4$\\u0005WR>$\\u0018BMq5\\u0018\\u0002Ft2\\u000fW^k&\\u0000RZ?;NWK1;E^\\u0018cx\\u0013HYw9\\u000eVC}+\\u0010[Dd8\\u001eWD\\u007f#\\u001fAI?yB\\u0000O\\u007f.FG\\u001a3&\\u000eSI>#\\u000eBYt2\\u0015_\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[96] = (new String(var10004)).intern();\n      var10003 = \"\\u00056x\\u0010\\\\\\u001cl*\\u001cC\\u0019q8\\u0007W[m.GE[\\u007f&\\u0012\\u0005G}$\\u001fTN}q\\u001dYFf \\u0011]\\u001f20\\u0010CAbw\\u001d\\u0001@1p\\u0007\\b\\u001fi0BFCe)\\u0015\\u0005\\u0010dq\\u0019KX6t\\u001f\\u0005Po-A[L1t\\u000fCZnv\\u001d\\u0003O`tC\\bJ7&\\u001c\\u0000\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[97] = (new String(var10004)).intern();\n      var10003 = \"\\ufffb\\u0001\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[98] = (new String(var10004)).intern();\n      var10003 = \"\\u0005cp\\rURr)\\u001cKG1w\\u0015WRo8GWZu)\\u001eR@j2\\u0007AM}-BKOm4\\u0003\\u0001Zj-\\u0015E\\u001cft\\u000e\\u0006Ghy\\u000eI_7.\\u001aC^\\u007f\\\"\\u001e]_?5B^Ot3\\u0013FKh3\\u0018ZKp \\u0003P\\\\3;AR\\u001fm'\\u0002TJ0'\\u0007\\u0000\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[99] = (new String(var10004)).intern();\n      var10003 = \"ￚￊ\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[100] = (new String(var10004)).intern();\n      var10003 = \"\\\\￪\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[101] = (new String(var10004)).intern();\n      var10003 = \"\\u0005e6\\u001a^B\\u007f3\\u0011\\u0001\\u001at%\\u0014EZbu\\u001c\\u0000\\u0010cy\\u0016RQdw\\u001c\\u0007Q1/\\u001b\\u0005P02\\rANc-\\u0011^Jnq\\u0004X^1t\\u0010KAn$B\\u0000\\u001db+\\u0012CR}1BPK42\\u0011\\u0002\\u0018h7\\u0002\\u0003@o.ABQwr\\u0006ZC7&\\u0002Z\\u001f\\u007f2\\u0004V\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[102] = (new String(var10004)).intern();\n      var10003 = \"\\u0019m&\\u0001\\u0006\\u001bd0CZEs+\\u0010\\u0007P7*\\u0005\\u0004^atD]D1v\\u0003\\\\^3p\\u0010\\u0004Ma9\\u0011AAo;\\u0019S\\u001bo)\\u0012I\\\\i7\\u000eIYw1\\u0016AEr*\\u0001\\tOv&GAJk.AAXcq\\u000fT\\u001fe(\\u0018\\u0006Xj9\\u001eIK`4\\u0001AOf&\\u0000\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[103] = (new String(var10004)).intern();\n      var10003 = \"ﾧ\\\"\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[104] = (new String(var10004)).intern();\n      var10003 = \"K`7\\rEDo%\\u000f^D`;\\u0000\\u0006]ku\\u0005YJk#GB\\u001cf/\\u0015COe+EKMr.BY_~4\\u0011\\u0001\\\\m.\\u001e\\u0001K\\u007f'\\u0018S\\u001b~7\\u0012KIit\\u0011G\\\\br\\u0015_B6*\\u001aDKj+\\u001eG_w1\\u0012\\u0005\\u001ao-\\u0016FE\\u007f\\\"\\u0004[Ir,\\u0001\".toCharArray();\n      var10005 = var10003.length;\n      var10004 = var10003;\n      var12 = var10005;\n\n      for(var2 = 0; var12 > var2; ++var2) {\n         var10007 = var10004[var2];\n         switch (var2 % 5) {\n            case 0:\n               var10008 = 40;\n               break;\n            case 1:\n               var10008 = 7;\n               break;\n            case 2:\n               var10008 = 65;\n               break;\n            case 3:\n               var10008 = 119;\n               break;\n            default:\n               var10008 = 49;\n         }\n\n         var10004[var2] = (char)(var10007 ^ var10008);\n      }\n\n      var10000[105] = (new String(var10004)).intern();\n      i = var10000;\n      char[] var6 = \"{O\\u0000Z\\u0004\\u00195\".toCharArray();\n      int var10002 = var6.length;\n      char[] var10001 = var6;\n      int var7 = var10002;\n\n      char var15;\n      byte var16;\n      for(var2 = 0; var7 > var2; ++var2) {\n         var15 = var10001[var2];\n         switch (var2 % 5) {\n            case 0:\n               var16 = 40;\n               break;\n            case 1:\n               var16 = 7;\n               break;\n            case 2:\n               var16 = 65;\n               break;\n            case 3:\n               var16 = 119;\n               break;\n            default:\n               var16 = 49;\n         }\n\n         var10001[var2] = (char)(var15 ^ var16);\n      }\n\n      a = (new String(var10001)).intern();\n      var6 = \"}S\\u0007Z\\t\".toCharArray();\n      var10002 = var6.length;\n      var10001 = var6;\n      var7 = var10002;\n\n      for(var2 = 0; var7 > var2; ++var2) {\n         var15 = var10001[var2];\n         switch (var2 % 5) {\n            case 0:\n               var16 = 40;\n               break;\n            case 1:\n               var16 = 7;\n               break;\n            case 2:\n               var16 = 65;\n               break;\n            case 3:\n               var16 = 119;\n               break;\n            default:\n               var16 = 49;\n         }\n\n         var10001[var2] = (char)(var15 ^ var16);\n      }\n\n      j = (new String(var10001)).intern();\n      f = \"\\b\";\n\n      label2227: {\n         String var10;\n         boolean var11;\n         try {\n            var10 = \"Bf7\\u0016\\u001f[b\\\"\\u0002CAs8Y|Mt2\\u0016VMC(\\u0010T[s\";\n         } catch (Exception var5) {\n            var11 = false;\n            break label2227;\n         }\n\n         var6 = var10.toCharArray();\n         var10002 = var6.length;\n         var10001 = var6;\n         var7 = var10002;\n\n         for(var2 = 0; var7 > var2; ++var2) {\n            var15 = var10001[var2];\n            switch (var2 % 5) {\n               case 0:\n                  var16 = 40;\n                  break;\n               case 1:\n                  var16 = 7;\n                  break;\n               case 2:\n                  var16 = 65;\n                  break;\n               case 3:\n                  var16 = 119;\n                  break;\n               default:\n                  var16 = 49;\n            }\n\n            var10001[var2] = (char)(var15 ^ var16);\n         }\n\n         var10 = (new String(var10001)).intern();\n\n         try {\n            Class.forName(var10);\n            var10 = \"Bf7\\u0016\\u001fEf5\\u001f\\u001fjn&>_\\\\b&\\u0012C\";\n         } catch (Exception var4) {\n            var11 = false;\n            break label2227;\n         }\n\n         var6 = var10.toCharArray();\n         var10002 = var6.length;\n         var10001 = var6;\n         var7 = var10002;\n\n         for(var2 = 0; var7 > var2; ++var2) {\n            var15 = var10001[var2];\n            switch (var2 % 5) {\n               case 0:\n                  var16 = 40;\n                  break;\n               case 1:\n                  var16 = 7;\n                  break;\n               case 2:\n                  var16 = 65;\n                  break;\n               case 3:\n                  var16 = 119;\n                  break;\n               default:\n                  var16 = 49;\n            }\n\n            var10001[var2] = (char)(var15 ^ var16);\n         }\n\n         var10 = (new String(var10001)).intern();\n\n         try {\n            Class.forName(var10);\n            \"\".getBytes(j);\n            b = MessageDigest.getInstance(a);\n            c = new Hashtable();\n            d = new Hashtable();\n            d.put(Byte.TYPE, \"B\");\n            d.put(Boolean.TYPE, \"Z\");\n            d.put(Short.TYPE, \"S\");\n            d.put(Character.TYPE, \"C\");\n            d.put(Integer.TYPE, \"I\");\n            d.put(Long.TYPE, \"J\");\n            d.put(Float.TYPE, \"F\");\n            d.put(Double.TYPE, \"D\");\n            g = new Hashtable();\n            h = new Hashtable();\n            a(c, b);\n            b(c, b);\n            c(c, b);\n            d(c, b);\n            e(c, b);\n            f(c, b);\n            g(c, b);\n            h(c, b);\n            i(c, b);\n            j(c, b);\n            return;\n         } catch (Exception var3) {\n            var11 = false;\n         }\n      }\n\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/c.java",
    "content": "@aa(\n   a = {ac.class}\n)\npublic class c implements ac {\n   public void a() throws Exception {\n      d.b();\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/d.java",
    "content": "import java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\npublic class d {\n   private static List<a<?, ?>> a = new ArrayList();\n   public static boolean b;\n\n   public static List<a<?, ?>> a() {\n      return a;\n   }\n\n   public static <K, V> a<K, V> a(String var0, int var1, f<K, V> var2, long var3, TimeUnit var5, g<V> var6, long var7, TimeUnit var9) {\n      e var10 = new e(var0, var1, TimeUnit.MILLISECONDS.convert(var3, var5), var2, var6, TimeUnit.MILLISECONDS.convert(var7, var9));\n      a.add(var10);\n      return var10;\n   }\n\n   public static <K, V> a<K, V> a(String var0, int var1, long var2, TimeUnit var4) {\n      e var5 = new e(var0, var1, TimeUnit.MILLISECONDS.convert(var2, var4), (f)null, (g)null, 10000L);\n      a.add(var5);\n      return var5;\n   }\n\n   public static void b() {\n      boolean var2 = b;\n      Iterator var0 = a().iterator();\n\n      while(var0.hasNext()) {\n         a var1 = (a)var0.next();\n         var1.i();\n         if (var2) {\n            break;\n         }\n      }\n\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/e.java",
    "content": "import java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport org.apache.commons.collections.map.LRUMap;\n\nclass e<K, V> implements a<K, V> {\n   private static final int a = 25;\n   protected int b;\n   protected f<K, V> c;\n   protected Map<K, b<K, V>> d;\n   protected ah e = new ah();\n   protected ah f = new ah();\n   protected List<Long> g = new ArrayList(25);\n   protected List<Long> h = new ArrayList(25);\n   protected Date i = null;\n   protected final String j;\n   protected final long k;\n   protected final g<V> l;\n   private final long m;\n\n   public e(String var1, int var2, long var3, f<K, V> var5, g<V> var6, long var7) {\n      this.j = var1;\n      this.b = var2;\n      this.m = var7;\n      if (var2 > 0) {\n         this.d = Collections.synchronizedMap(new LRUMap(var2));\n      } else {\n         this.d = Collections.synchronizedMap(new HashMap(var2));\n      }\n\n      this.k = var3;\n      this.c = var5;\n      this.l = var6;\n   }\n\n   public String a() {\n      return this.j;\n   }\n\n   public int b() {\n      return this.b;\n   }\n\n   public int c() {\n      return this.d.size();\n   }\n\n   public long d() {\n      return this.e.b() + this.f.b();\n   }\n\n   public Long f() {\n      long var1 = this.e.b();\n      long var3 = this.f.b();\n\n      long var10000;\n      try {\n         if (var1 + var3 == 0L) {\n            var10000 = 0L;\n            return var10000;\n         }\n      } catch (a_ var5) {\n         throw var5;\n      }\n\n      var10000 = Math.round(100.0 * (double)var1 / (double)(var1 + var3));\n      return var10000;\n   }\n\n   public Date h() {\n      return this.i;\n   }\n\n   public void i() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public void j() {\n      this.d.clear();\n      this.f.c();\n      this.e.c();\n      this.i = new Date();\n   }\n\n   public V a(K var1) {\n      return this.a(var1, this.c);\n   }\n\n   public boolean c(K var1) {\n      return this.d.containsKey(var1);\n   }\n\n   public V a(K param1, f<K, V> param2) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public void a(K var1, V var2) {\n      b var10000;\n      b var10001;\n      Object var10002;\n      Object var10003;\n      long var10004;\n      label16: {\n         try {\n            var10000 = new b;\n            var10001 = var10000;\n            var10002 = var1;\n            var10003 = var2;\n            if (this.k > 0L) {\n               var10004 = this.k + System.currentTimeMillis();\n               break label16;\n            }\n         } catch (a_ var4) {\n            throw var4;\n         }\n\n         var10004 = 0L;\n      }\n\n      var10001.<init>(var10002, var10003, var10004, this.m + System.currentTimeMillis());\n      b var3 = var10000;\n      this.d.put(var1, var3);\n   }\n\n   public void b(K var1) {\n      this.d.remove(var1);\n   }\n\n   public Iterator<K> k() {\n      return this.d.keySet().iterator();\n   }\n\n   public List<b<K, V>> l() {\n      return new ArrayList(this.d.values());\n   }\n\n   public List<Long> e() {\n      return this.g;\n   }\n\n   public List<Long> g() {\n      return this.h;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/f.java",
    "content": "public interface f<K, V> {\n   V a(K var1);\n}\n"
  },
  {
    "path": "testData/obfuscated/g.java",
    "content": "public interface g<V> {\n   boolean a(V var1);\n}\n"
  },
  {
    "path": "testData/obfuscated/h.java",
    "content": "import java.util.concurrent.atomic.AtomicLong;\n\npublic class h {\n   private final AtomicLong a = new AtomicLong();\n\n   public long a() {\n      boolean var5 = n.c;\n      long var1 = this.a.incrementAndGet();\n\n      long var10000;\n      label48: {\n         try {\n            var10000 = var1;\n            if (var5) {\n               return var10000;\n            }\n\n            if (var1 <= 9223372036854775797L) {\n               break label48;\n            }\n         } catch (a_ var8) {\n            throw var8;\n         }\n\n         AtomicLong var3;\n         synchronized(var3 = this.a) {\n            AtomicLong var9 = this.a;\n            if (!var5) {\n               try {\n                  if (var9.get() > 9223372036854775797L) {\n                     this.a.set(0L);\n                  }\n               } catch (a_ var6) {\n                  throw var6;\n               }\n\n               var9 = var3;\n            }\n\n         }\n      }\n\n      var10000 = var1;\n      return var10000;\n   }\n\n   public String b() {\n      return String.valueOf(this.a());\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/i.java",
    "content": "import java.util.Collection;\nimport java.util.Iterator;\n\npublic abstract class i<T> implements k<T> {\n   public void a(Collection<? extends T> var1) {\n      boolean var4 = n.c;\n      Iterator var2 = var1.iterator();\n\n      while(var2.hasNext()) {\n         Object var3 = var2.next();\n         this.a(var3);\n         if (var4) {\n            break;\n         }\n      }\n\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/j.java",
    "content": "class j extends i<ak> {\n   final ai a;\n\n   j(ai var1) {\n      this.a = var1;\n   }\n\n   public void a(ak var1) {\n      al var2 = (al)ai.b().get(var1);\n\n      al var10000;\n      label21: {\n         label20: {\n            try {\n               var10000 = var2;\n               if (an.k) {\n                  break label21;\n               }\n\n               if (var2 != null) {\n                  break label20;\n               }\n            } catch (a_ var3) {\n               throw var3;\n            }\n\n            var2 = new al(var1);\n            ai.b().put(var1, var2);\n         }\n\n         var10000 = var2;\n      }\n\n      var10000.a(var1.d());\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/k.java",
    "content": "import java.util.Collection;\n\npublic interface k<T> {\n   void a(T var1);\n\n   void a(Collection<? extends T> var1);\n}\n"
  },
  {
    "path": "testData/obfuscated/l.java",
    "content": "import java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\npublic class l<T> implements k<T> {\n   private List<T> a = new ArrayList();\n\n   public void a(T var1) {\n      this.a.add(var1);\n   }\n\n   public void a(Collection<? extends T> var1) {\n      this.a.addAll(var1);\n   }\n\n   public List<T> a() {\n      return this.a;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/m.java",
    "content": "import java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\npublic class m<T> implements k<T> {\n   private Set<T> a = new LinkedHashSet();\n\n   public void a(T var1) {\n      this.a.add(var1);\n   }\n\n   public void a(Collection<? extends T> var1) {\n      this.a.addAll(var1);\n   }\n\n   public Set<T> a() {\n      return this.a;\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/n.java",
    "content": "import java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\npublic class n<F, S> {\n   private F a;\n   private S b;\n   public static boolean c;\n   private static final String d;\n\n   public n() {\n   }\n\n   public n(F var1) {\n      this.a = var1;\n   }\n\n   public n(F var1, S var2) {\n      this.a = var1;\n      this.b = var2;\n   }\n\n   public F a() {\n      return this.a;\n   }\n\n   public void a(F var1) {\n      this.a = var1;\n   }\n\n   public S b() {\n      return this.b;\n   }\n\n   public void b(S var1) {\n      this.b = var1;\n   }\n\n   public boolean equals(Object param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   private boolean a(Object param1, Object param2) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public String toString() {\n      return this.a + d + this.b;\n   }\n\n   public int hashCode() {\n      Object var10000;\n      label28: {\n         try {\n            if (this.a == null) {\n               var10000 = \"\";\n               break label28;\n            }\n         } catch (a_ var2) {\n            throw var2;\n         }\n\n         var10000 = this.a;\n      }\n\n      Object var10001;\n      int var3;\n      try {\n         var3 = var10000.hashCode() / 2;\n         if (this.b == null) {\n            var10001 = \"\";\n            return var3 + var10001.hashCode() / 2;\n         }\n      } catch (a_ var1) {\n         throw var1;\n      }\n\n      var10001 = this.b;\n      return var3 + var10001.hashCode() / 2;\n   }\n\n   public static <T extends n<K, V>, K, V> List<K> a(Collection<T> var0) {\n      ArrayList var1 = new ArrayList(var0.size());\n      Iterator var2 = var0.iterator();\n\n      while(var2.hasNext()) {\n         n var3 = (n)var2.next();\n         var1.add(var3.a());\n      }\n\n      return var1;\n   }\n\n   public static <T extends n<K, V>, K, V> List<V> b(Collection<T> param0) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public static <K, V> List<n<K, V>> a(Map<K, V> var0) {\n      boolean var4 = c;\n      ArrayList var1 = new ArrayList(var0.size());\n      Iterator var2 = var0.entrySet().iterator();\n\n      ArrayList var10000;\n      while(true) {\n         if (var2.hasNext()) {\n            Map.Entry var3 = (Map.Entry)var2.next();\n\n            try {\n               var10000 = var1;\n               if (var4) {\n                  break;\n               }\n\n               var1.add(new n(var3.getKey(), var3.getValue()));\n               if (!var4) {\n                  continue;\n               }\n            } catch (a_ var6) {\n               throw var6;\n            }\n\n            int var5 = ap.c;\n            ++var5;\n            ap.c = var5;\n         }\n\n         var10000 = var1;\n         break;\n      }\n\n      return var10000;\n   }\n\n   public static <K, V> Map<K, V> c(Collection<n<K, V>> var0) {\n      HashMap var1 = new HashMap();\n      Iterator var2 = var0.iterator();\n\n      while(var2.hasNext()) {\n         n var3 = (n)var2.next();\n         var1.put(var3.a(), var3.b());\n      }\n\n      return var1;\n   }\n\n   static {\n      char[] var10000 = \"p\\u001c\".toCharArray();\n      int var10002 = var10000.length;\n      int var1 = 0;\n      char[] var10001 = var10000;\n      int var2 = var10002;\n      int var10003;\n      char[] var4;\n      if (var10002 <= 1) {\n         var4 = var10000;\n         var10003 = var1;\n      } else {\n         var10001 = var10000;\n         var2 = var10002;\n         if (var10002 <= var1) {\n            d = (new String(var10000)).intern();\n            return;\n         }\n\n         var4 = var10000;\n         var10003 = var1;\n      }\n\n      while(true) {\n         char var10004 = var4[var10003];\n         byte var10005;\n         switch (var1 % 5) {\n            case 0:\n               var10005 = 74;\n               break;\n            case 1:\n               var10005 = 60;\n               break;\n            case 2:\n               var10005 = 116;\n               break;\n            case 3:\n               var10005 = 28;\n               break;\n            default:\n               var10005 = 38;\n         }\n\n         var4[var10003] = (char)(var10004 ^ var10005);\n         ++var1;\n         if (var2 == 0) {\n            var10003 = var2;\n            var4 = var10001;\n         } else {\n            if (var2 <= var1) {\n               d = (new String(var10001)).intern();\n               return;\n            }\n\n            var4 = var10001;\n            var10003 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/o.java",
    "content": "import java.math.BigDecimal;\nimport java.util.regex.Pattern;\n\npublic class o {\n   private Object a;\n   private static final Pattern b;\n   private static final String c;\n\n   public boolean a() {\n      boolean var10000;\n      try {\n         if (this.a == null) {\n            var10000 = true;\n            return var10000;\n         }\n      } catch (IllegalArgumentException var1) {\n         throw var1;\n      }\n\n      var10000 = false;\n      return var10000;\n   }\n\n   public boolean b() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public boolean c() {\n      boolean var10000;\n      try {\n         if (!this.b()) {\n            var10000 = true;\n            return var10000;\n         }\n      } catch (IllegalArgumentException var1) {\n         throw var1;\n      }\n\n      var10000 = false;\n      return var10000;\n   }\n\n   public o a(String... var1) {\n      try {\n         if (this.b()) {\n            return this;\n         }\n      } catch (IllegalArgumentException var7) {\n         throw var7;\n      }\n\n      String[] var2 = var1;\n      int var3 = var1.length;\n\n      for(int var4 = 0; var4 < var3; ++var4) {\n         String var5 = var2[var4];\n\n         try {\n            if (this.a.equals(var5)) {\n               return b((Object)null);\n            }\n         } catch (IllegalArgumentException var6) {\n            throw var6;\n         }\n      }\n\n      return this;\n   }\n\n   public boolean d() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public Object e() {\n      return this.a;\n   }\n\n   public Object a(Object var1) {\n      Object var10000;\n      try {\n         if (this.a == null) {\n            var10000 = var1;\n            return var10000;\n         }\n      } catch (IllegalArgumentException var2) {\n         throw var2;\n      }\n\n      var10000 = this.a;\n      return var10000;\n   }\n\n   public <T> T a(Class<?> param1, T param2) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public <V> V b(Class<V> param1, V param2) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public String f() {\n      String var10000;\n      try {\n         if (this.a()) {\n            var10000 = null;\n            return var10000;\n         }\n      } catch (IllegalArgumentException var1) {\n         throw var1;\n      }\n\n      var10000 = this.g();\n      return var10000;\n   }\n\n   public String g() {\n      String var10000;\n      try {\n         if (this.a == null) {\n            var10000 = \"\";\n            return var10000;\n         }\n      } catch (IllegalArgumentException var1) {\n         throw var1;\n      }\n\n      var10000 = this.a.toString();\n      return var10000;\n   }\n\n   public String a(String var1) {\n      String var10000;\n      try {\n         if (this.a()) {\n            var10000 = var1;\n            return var10000;\n         }\n      } catch (IllegalArgumentException var2) {\n         throw var2;\n      }\n\n      var10000 = this.g();\n      return var10000;\n   }\n\n   public boolean a(boolean var1) {\n      try {\n         if (this.a()) {\n            return var1;\n         }\n      } catch (IllegalArgumentException var2) {\n         throw var2;\n      }\n\n      try {\n         if (this.a instanceof Boolean) {\n            return (Boolean)this.a;\n         }\n      } catch (IllegalArgumentException var3) {\n         throw var3;\n      }\n\n      return Boolean.parseBoolean(String.valueOf(this.a));\n   }\n\n   public boolean h() {\n      return this.a(false);\n   }\n\n   public int a(int param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public Integer i() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public long a(long param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public Long j() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public double a(double param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public BigDecimal a(BigDecimal param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public static o b(Object var0) {\n      o var1 = new o();\n      var1.a = var0;\n      return var1;\n   }\n\n   public String toString() {\n      return this.g();\n   }\n\n   public <E extends Enum<E>> E a(Class<E> var1) {\n      try {\n         if (this.a == null) {\n            return null;\n         }\n      } catch (Exception var4) {\n         throw var4;\n      }\n\n      try {\n         if (var1.isAssignableFrom(this.a.getClass())) {\n            return (Enum)this.a;\n         }\n      } catch (Exception var5) {\n         throw var5;\n      }\n\n      try {\n         return Enum.valueOf(var1, String.valueOf(this.a));\n      } catch (Exception var3) {\n         return null;\n      }\n   }\n\n   public String b(int var1) {\n      String var2 = this.g();\n\n      try {\n         if (var2 == null) {\n            return null;\n         }\n      } catch (IllegalArgumentException var3) {\n         throw var3;\n      }\n\n      if (var1 < 0) {\n         var1 *= -1;\n\n         try {\n            if (var2.length() < var1) {\n               return \"\";\n            }\n         } catch (IllegalArgumentException var4) {\n            throw var4;\n         }\n\n         return var2.substring(var1);\n      } else {\n         try {\n            if (var2.length() < var1) {\n               return var2;\n            }\n         } catch (IllegalArgumentException var5) {\n            throw var5;\n         }\n\n         return var2.substring(0, var1);\n      }\n   }\n\n   public String c(int var1) {\n      String var2 = this.g();\n\n      try {\n         if (var2 == null) {\n            return null;\n         }\n      } catch (IllegalArgumentException var3) {\n         throw var3;\n      }\n\n      if (var1 < 0) {\n         var1 *= -1;\n\n         try {\n            if (var2.length() < var1) {\n               return var2;\n            }\n         } catch (IllegalArgumentException var4) {\n            throw var4;\n         }\n\n         return var2.substring(0, var2.length() - var1);\n      } else {\n         try {\n            if (var2.length() < var1) {\n               return var2;\n            }\n         } catch (IllegalArgumentException var5) {\n            throw var5;\n         }\n\n         return var2.substring(var2.length() - var1);\n      }\n   }\n\n   public String a(int var1, int var2) {\n      String var3 = this.g();\n\n      try {\n         if (var3 == null) {\n            return null;\n         }\n      } catch (IllegalArgumentException var4) {\n         throw var4;\n      }\n\n      try {\n         if (var1 > var3.length()) {\n            return \"\";\n         }\n      } catch (IllegalArgumentException var5) {\n         throw var5;\n      }\n\n      return var3.substring(var1, Math.min(var3.length(), var2));\n   }\n\n   public int k() {\n      String var1 = this.g();\n\n      try {\n         if (var1 == null) {\n            return 0;\n         }\n      } catch (IllegalArgumentException var2) {\n         throw var2;\n      }\n\n      return var1.length();\n   }\n\n   public boolean b(Class<?> param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   static {\n      char[] var10000;\n      int var1;\n      char[] var10001;\n      int var10002;\n      int var2;\n      int var10003;\n      char[] var4;\n      char var10004;\n      byte var10005;\n      label51: {\n         var10000 = \"\\t!\\\"\\u0000r>`/\\u0001s<%>\\u001a=>/vN\".toCharArray();\n         var10002 = var10000.length;\n         var1 = 0;\n         var10001 = var10000;\n         var2 = var10002;\n         if (var10002 <= 1) {\n            var4 = var10000;\n            var10003 = var1;\n         } else {\n            var10001 = var10000;\n            var2 = var10002;\n            if (var10002 <= var1) {\n               break label51;\n            }\n\n            var4 = var10000;\n            var10003 = var1;\n         }\n\n         while(true) {\n            var10004 = var4[var10003];\n            switch (var1 % 5) {\n               case 0:\n                  var10005 = 74;\n                  break;\n               case 1:\n                  var10005 = 64;\n                  break;\n               case 2:\n                  var10005 = 76;\n                  break;\n               case 3:\n                  var10005 = 110;\n                  break;\n               default:\n                  var10005 = 29;\n            }\n\n            var4[var10003] = (char)(var10004 ^ var10005);\n            ++var1;\n            if (var2 == 0) {\n               var10003 = var2;\n               var4 = var10001;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10001;\n               var10003 = var1;\n            }\n         }\n      }\n\n      c = (new String(var10001)).intern();\n      var10000 = \"\\u0016$gFAd\\u001c(E4u\".toCharArray();\n      var10002 = var10000.length;\n      var1 = 0;\n      var10001 = var10000;\n      var2 = var10002;\n      if (var10002 <= 1) {\n         var4 = var10000;\n         var10003 = var1;\n      } else {\n         var10001 = var10000;\n         var2 = var10002;\n         if (var10002 <= var1) {\n            b = Pattern.compile((new String(var10000)).intern());\n            return;\n         }\n\n         var4 = var10000;\n         var10003 = var1;\n      }\n\n      while(true) {\n         var10004 = var4[var10003];\n         switch (var1 % 5) {\n            case 0:\n               var10005 = 74;\n               break;\n            case 1:\n               var10005 = 64;\n               break;\n            case 2:\n               var10005 = 76;\n               break;\n            case 3:\n               var10005 = 110;\n               break;\n            default:\n               var10005 = 29;\n         }\n\n         var4[var10003] = (char)(var10004 ^ var10005);\n         ++var1;\n         if (var2 == 0) {\n            var10003 = var2;\n            var4 = var10001;\n         } else {\n            if (var2 <= var1) {\n               b = Pattern.compile((new String(var10001)).intern());\n               return;\n            }\n\n            var4 = var10001;\n            var10003 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/okhttp3/internal/connection/e.java",
    "content": "package okhttp3.internal.connection;\n\nimport java.io.IOException;\nimport java.lang.ref.Reference;\nimport java.net.ConnectException;\nimport java.net.Proxy;\nimport java.net.Socket;\nimport java.net.SocketException;\nimport java.net.SocketTimeoutException;\nimport java.net.Proxy.Type;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\nimport javax.net.ssl.SSLPeerUnverifiedException;\nimport javax.net.ssl.SSLSession;\nimport okhttp3.HttpUrl;\nimport okhttp3.OkHttpClient;\nimport okhttp3.Protocol;\nimport okhttp3.internal.http2.ConnectionShutdownException;\nimport okhttp3.internal.http2.ErrorCode;\nimport okhttp3.internal.http2.StreamResetException;\n\npublic final class e extends okhttp3.internal.http2.d.c implements okhttp3.j {\n   static final boolean h = true;\n   private Protocol A;\n   private okhttp3.internal.http2.d B;\n   private okio.e C;\n   private okio.d D;\n   private int E;\n   private int F = 1;\n   public final okhttp3.internal.connection.f b;\n   boolean c;\n   int d;\n   int e;\n   final List<Reference<okhttp3.internal.connection.i>> f = new ArrayList();\n   long g = 9223372036854775807L;\n   private final okhttp3.ai w;\n   private Socket x;\n   private Socket y;\n   private okhttp3.u z;\n\n   public e(okhttp3.internal.connection.f var1, okhttp3.ai var2) {\n      this.b = var1;\n      this.w = var2;\n   }\n\n   private void G(int var1, int var2, int var3, okhttp3.f var4, okhttp3.r var5) throws IOException {\n      okhttp3.ae var6 = this.N();\n      HttpUrl var7 = var6.i();\n\n      for(int var8 = 0; var8 < 21; ++var8) {\n         this.H(var1, var2, var4, var5);\n         var6 = this.M(var2, var3, var6, var7);\n         if (var6 == null) {\n            break;\n         }\n\n         okhttp3.internal.c.m(this.x);\n         this.x = null;\n         this.D = null;\n         this.C = null;\n         var5.g(var4, this.w.f(), this.w.e(), (Protocol)null);\n         OkHttpClient.a.g(var4, this.w.f(), this.w.e(), (Protocol)null);\n      }\n\n   }\n\n   private void H(int var1, int var2, okhttp3.f var3, okhttp3.r var4) throws IOException {\n      Proxy var5 = this.w.e();\n      okhttp3.a var6 = this.w.d();\n      Socket var13;\n      if (var5.type() != Type.DIRECT && var5.type() != Type.HTTP) {\n         var13 = new Socket(var5);\n      } else {\n         var13 = var6.n().createSocket();\n      }\n\n      this.x = var13;\n      var4.d(var3, this.w.f(), var5);\n      OkHttpClient.a.d(var3, this.w.f(), var5);\n      this.x.setSoTimeout(var2);\n\n      StringBuilder var11;\n      ConnectException var12;\n      try {\n         okhttp3.internal.e.e.n().a(this.x, this.w.f(), var1);\n      } catch (ConnectException var9) {\n         var11 = new StringBuilder();\n         var11.append(\"Failed to connect to \");\n         var11.append(this.w.f());\n         var12 = new ConnectException(var11.toString());\n         var12.initCause(var9);\n         throw var12;\n      } catch (NullPointerException var10) {\n         var11 = new StringBuilder();\n         var11.append(\"Failed to connect to \");\n         var11.append(this.w.f());\n         var12 = new ConnectException(var11.toString());\n         var12.initCause(var10);\n         throw var12;\n      }\n\n      try {\n         this.C = okio.m.b(okio.m.k(this.x));\n         this.D = okio.m.c(okio.m.e(this.x));\n      } catch (NullPointerException var7) {\n         throw new IOException(var7);\n      } catch (IllegalArgumentException var8) {\n         throw new IOException(var8);\n      }\n   }\n\n   private void I(okhttp3.internal.connection.b var1, int var2, okhttp3.f var3, okhttp3.r var4) throws IOException {\n      if (this.w.d().t() == null) {\n         if (this.w.d().p().contains(Protocol.H2_PRIOR_KNOWLEDGE)) {\n            this.y = this.x;\n            this.A = Protocol.H2_PRIOR_KNOWLEDGE;\n            this.J(var2);\n         } else {\n            this.y = this.x;\n            this.A = Protocol.HTTP_1_1;\n         }\n      } else {\n         var4.e(var3);\n         OkHttpClient.a.e(var3);\n         this.K(var1);\n         var4.f(var3, this.z);\n         OkHttpClient.a.f(var3, this.z);\n         if (this.A == Protocol.HTTP_2) {\n            this.J(var2);\n         }\n\n      }\n   }\n\n   private void J(int var1) throws IOException {\n      this.y.setSoTimeout(0);\n      okhttp3.internal.http2.d var2 = (new okhttp3.internal.http2.d.a(true)).i(this.y, this.w.d().l().j(), this.C, this.D).j(this).k(var1).l();\n      this.B = var2;\n      var2.K();\n   }\n\n   private void K(okhttp3.internal.connection.b param1) throws IOException {\n      // $FF: Couldn't be decompiled\n   }\n\n   private boolean L(SSLSession var1) {\n      boolean var2;\n      if (!\"NONE\".equals(var1.getProtocol()) && !\"SSL_NULL_WITH_NULL_NULL\".equals(var1.getCipherSuite())) {\n         var2 = true;\n      } else {\n         var2 = false;\n      }\n\n      return var2;\n   }\n\n   private okhttp3.ae M(int var1, int var2, okhttp3.ae var3, HttpUrl var4) throws IOException {\n      StringBuilder var5 = new StringBuilder();\n      var5.append(\"CONNECT \");\n      var5.append(okhttp3.internal.c.u(var4, true));\n      var5.append(\" HTTP/1.1\");\n      String var10 = var5.toString();\n\n      okhttp3.ag var11;\n      do {\n         okhttp3.internal.c.a var6 = new okhttp3.internal.c.a((OkHttpClient)null, (okhttp3.internal.connection.e)null, this.C, this.D);\n         this.C.k().c((long)var1, TimeUnit.MILLISECONDS);\n         this.D.k().c((long)var2, TimeUnit.MILLISECONDS);\n         var6.p(var3.k(), var10);\n         var6.e();\n         var11 = var6.f(false).n(var3).C();\n         var6.s(var11);\n         int var7 = var11.p();\n         if (var7 == 200) {\n            if (this.C.f().j() && this.D.e().j()) {\n               return null;\n            }\n\n            IOException var9 = new IOException(\"TLS tunnel buffered too many bytes!\");\n            throw var9;\n         }\n\n         if (var7 != 407) {\n            StringBuilder var8 = new StringBuilder();\n            var8.append(\"Unexpected response code for CONNECT: \");\n            var8.append(var11.p());\n            throw new IOException(var8.toString());\n         }\n\n         var3 = this.w.d().o().b(this.w, var11);\n         if (var3 == null) {\n            throw new IOException(\"Failed to authenticate with proxy\");\n         }\n      } while(!\"close\".equalsIgnoreCase(var11.t(\"Connection\")));\n\n      return var3;\n   }\n\n   private okhttp3.ae N() throws IOException {\n      okhttp3.ae var1 = (new okhttp3.ae.a()).i(this.w.d().l()).q(\"CONNECT\", (okhttp3.af)null).k(\"Host\", okhttp3.internal.c.u(this.w.d().l(), true)).k(\"Proxy-Connection\", \"Keep-Alive\").k(\"User-Agent\", okhttp3.internal.d.a()).v();\n      okhttp3.ag var2 = (new okhttp3.ag.a()).n(var1).o(Protocol.HTTP_1_1).p(407).q(\"Preemptive Authenticate\").v(okhttp3.internal.c.d).z(-1L).A(-1L).s(\"Proxy-Authenticate\", \"OkHttp-Preemptive\").C();\n      okhttp3.ae var3 = this.w.d().o().b(this.w, var2);\n      if (var3 != null) {\n         var1 = var3;\n      }\n\n      return var1;\n   }\n\n   private boolean O(List<okhttp3.ai> var1) {\n      int var2 = var1.size();\n\n      for(int var3 = 0; var3 < var2; ++var3) {\n         okhttp3.ai var4 = (okhttp3.ai)var1.get(var3);\n         if (var4.e().type() == Type.DIRECT && this.w.e().type() == Type.DIRECT && this.w.f().equals(var4.f())) {\n            return true;\n         }\n      }\n\n      return false;\n   }\n\n   public okhttp3.ai a() {\n      return this.w;\n   }\n\n   public void i() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public void j(int param1, int param2, int param3, int param4, boolean param5, okhttp3.f param6, okhttp3.r param7) {\n      // $FF: Couldn't be decompiled\n   }\n\n   boolean k(okhttp3.a var1, List<okhttp3.ai> var2) {\n      if (this.f.size() < this.F && !this.c) {\n         if (!okhttp3.internal.a.i.d(this.w.d(), var1)) {\n            return false;\n         }\n\n         if (var1.l().j().equals(this.a().d().l().j())) {\n            return true;\n         }\n\n         if (this.B == null) {\n            return false;\n         }\n\n         if (var2 != null && this.O(var2)) {\n            if (var1.u() != okhttp3.internal.g.d.a) {\n               return false;\n            }\n\n            if (!this.l(var1.l())) {\n               return false;\n            }\n\n            try {\n               var1.v().d(var1.l().j(), this.s().e());\n               return true;\n            } catch (SSLPeerUnverifiedException var3) {\n            }\n         }\n      }\n\n      return false;\n   }\n\n   public boolean l(HttpUrl var1) {\n      int var2 = var1.k();\n      int var3 = this.w.d().l().k();\n      boolean var4 = false;\n      if (var2 != var3) {\n         return false;\n      } else if (!var1.j().equals(this.w.d().l().j())) {\n         boolean var5 = var4;\n         if (this.z != null) {\n            var5 = var4;\n            if (okhttp3.internal.g.d.a.b(var1.j(), (X509Certificate)this.z.e().get(0))) {\n               var5 = true;\n            }\n         }\n\n         return var5;\n      } else {\n         return true;\n      }\n   }\n\n   okhttp3.internal.b.c m(OkHttpClient var1, okhttp3.z.a var2) throws SocketException {\n      if (this.B != null) {\n         return new okhttp3.internal.http2.e(var1, this, var2, this.B);\n      } else {\n         this.y.setSoTimeout(var2.f());\n         this.C.k().c((long)var2.f(), TimeUnit.MILLISECONDS);\n         this.D.k().c((long)var2.g(), TimeUnit.MILLISECONDS);\n         return new okhttp3.internal.c.a(var1, this, this.C, this.D);\n      }\n   }\n\n   public void n() {\n      okhttp3.internal.c.m(this.x);\n   }\n\n   public Socket o() {\n      return this.y;\n   }\n\n   public boolean p(boolean var1) {\n      if (!this.y.isClosed() && !this.y.isInputShutdown() && !this.y.isOutputShutdown()) {\n         okhttp3.internal.http2.d var2 = this.B;\n         if (var2 != null) {\n            return var2.M(System.nanoTime());\n         } else if (var1) {\n            boolean var10001;\n            int var3;\n            try {\n               var3 = this.y.getSoTimeout();\n            } catch (SocketTimeoutException var21) {\n               var10001 = false;\n               return true;\n            } catch (IOException var22) {\n               var10001 = false;\n               return false;\n            }\n\n            boolean var13 = false;\n\n            try {\n               var13 = true;\n               this.y.setSoTimeout(1);\n               var1 = this.C.j();\n               var13 = false;\n            } finally {\n               if (var13) {\n                  try {\n                     this.y.setSoTimeout(var3);\n                  } catch (SocketTimeoutException var14) {\n                     var10001 = false;\n                     return true;\n                  } catch (IOException var15) {\n                     var10001 = false;\n                     return false;\n                  }\n               }\n            }\n\n            if (var1) {\n               try {\n                  this.y.setSoTimeout(var3);\n                  return false;\n               } catch (SocketTimeoutException var16) {\n                  var10001 = false;\n                  return true;\n               } catch (IOException var17) {\n                  var10001 = false;\n               }\n            } else {\n               try {\n                  this.y.setSoTimeout(var3);\n                  return true;\n               } catch (SocketTimeoutException var18) {\n                  var10001 = false;\n                  return true;\n               } catch (IOException var19) {\n                  var10001 = false;\n               }\n            }\n\n            return false;\n         } else {\n            return true;\n         }\n      } else {\n         return false;\n      }\n   }\n\n   public void q(okhttp3.internal.http2.g var1) throws IOException {\n      var1.r(ErrorCode.REFUSED_STREAM, (IOException)null);\n   }\n\n   public void r(okhttp3.internal.http2.d param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public okhttp3.u s() {\n      return this.z;\n   }\n\n   public boolean t() {\n      boolean var1;\n      if (this.B != null) {\n         var1 = true;\n      } else {\n         var1 = false;\n      }\n\n      return var1;\n   }\n\n   public String toString() {\n      StringBuilder var1 = new StringBuilder();\n      var1.append(\"Connection{\");\n      var1.append(this.w.d().l().j());\n      var1.append(\":\");\n      var1.append(this.w.d().l().k());\n      var1.append(\", proxy=\");\n      var1.append(this.w.e());\n      var1.append(\" hostAddress=\");\n      var1.append(this.w.f());\n      var1.append(\" cipherSuite=\");\n      okhttp3.u var2 = this.z;\n      Object var3;\n      if (var2 != null) {\n         var3 = var2.d();\n      } else {\n         var3 = \"none\";\n      }\n\n      var1.append(var3);\n      var1.append(\" protocol=\");\n      var1.append(this.A);\n      var1.append('}');\n      return var1.toString();\n   }\n\n   void u(IOException var1) {\n      if (!h && Thread.holdsLock(this.b)) {\n         throw new AssertionError();\n      } else {\n         okhttp3.internal.connection.f var2 = this.b;\n         synchronized(var2){}\n\n         Throwable var10000;\n         boolean var10001;\n         label870: {\n            label876: {\n               ErrorCode var94;\n               label877: {\n                  label867: {\n                     int var3;\n                     try {\n                        if (!(var1 instanceof StreamResetException)) {\n                           break label867;\n                        }\n\n                        var94 = ((StreamResetException)var1).errorCode;\n                        if (var94 != ErrorCode.REFUSED_STREAM) {\n                           break label877;\n                        }\n\n                        var3 = this.E + 1;\n                        this.E = var3;\n                     } catch (Throwable var93) {\n                        var10000 = var93;\n                        var10001 = false;\n                        break label870;\n                     }\n\n                     if (var3 > 1) {\n                        try {\n                           this.c = true;\n                           ++this.d;\n                        } catch (Throwable var89) {\n                           var10000 = var89;\n                           var10001 = false;\n                           break label870;\n                        }\n                     }\n                     break label876;\n                  }\n\n                  try {\n                     if (this.t() && !(var1 instanceof ConnectionShutdownException)) {\n                        break label876;\n                     }\n                  } catch (Throwable var92) {\n                     var10000 = var92;\n                     var10001 = false;\n                     break label870;\n                  }\n\n                  try {\n                     this.c = true;\n                     if (this.e != 0) {\n                        break label876;\n                     }\n                  } catch (Throwable var91) {\n                     var10000 = var91;\n                     var10001 = false;\n                     break label870;\n                  }\n\n                  if (var1 != null) {\n                     try {\n                        this.b.k(this.w, var1);\n                     } catch (Throwable var88) {\n                        var10000 = var88;\n                        var10001 = false;\n                        break label870;\n                     }\n                  }\n\n                  try {\n                     ++this.d;\n                     break label876;\n                  } catch (Throwable var87) {\n                     var10000 = var87;\n                     var10001 = false;\n                     break label870;\n                  }\n               }\n\n               try {\n                  if (var94 != ErrorCode.CANCEL) {\n                     this.c = true;\n                     ++this.d;\n                  }\n               } catch (Throwable var90) {\n                  var10000 = var90;\n                  var10001 = false;\n                  break label870;\n               }\n            }\n\n            label836:\n            try {\n               return;\n            } catch (Throwable var86) {\n               var10000 = var86;\n               var10001 = false;\n               break label836;\n            }\n         }\n\n         while(true) {\n            Throwable var95 = var10000;\n\n            try {\n               throw var95;\n            } catch (Throwable var85) {\n               var10000 = var85;\n               var10001 = false;\n               continue;\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/p.java",
    "content": "import java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\n\npublic class p {\n   protected String a;\n   protected String b;\n   protected String c;\n   private String d;\n   private String e;\n   private Throwable f;\n   private static final String[] g;\n\n   public p(String var1, String var2, Throwable var3, String var4) {\n      int var10 = s.d;\n      super();\n      this.e = \"\";\n      this.a = var1;\n      this.c = var2;\n      this.f = var3;\n      ArrayList var5 = new ArrayList(Arrays.asList(Thread.currentThread().getStackTrace()));\n      int var6 = 3;\n\n      while(!var5.isEmpty()) {\n         try {\n            if (var6 <= 0) {\n               break;\n            }\n\n            var5.remove(0);\n            --var6;\n            if (var10 == 0) {\n               continue;\n            }\n         } catch (a_ var16) {\n            throw var16;\n         }\n\n         int var11 = ap.c;\n         ++var11;\n         ap.c = var11;\n         break;\n      }\n\n      StringBuilder var7 = new StringBuilder();\n      Iterator var8 = var5.iterator();\n\n      while(true) {\n         if (var8.hasNext()) {\n            StackTraceElement var9 = (StackTraceElement)var8.next();\n\n            try {\n               var7.append(var9.getClassName());\n               var7.append(\".\");\n               var7.append(var9.getMethodName());\n               var7.append(g[0]);\n               var7.append(var9.getFileName());\n               var7.append(\":\");\n               var7.append(var9.getLineNumber());\n               var7.append(\")\");\n               var7.append(\"\\n\");\n               if (var10 != 0) {\n                  break;\n               }\n\n               if (var10 == 0) {\n                  continue;\n               }\n            } catch (a_ var15) {\n               throw var15;\n            }\n         }\n\n         this.d = var7.toString();\n         break;\n      }\n\n      p var10000;\n      String var10001;\n      label56: {\n         label55: {\n            label54: {\n               try {\n                  if (var10 != 0) {\n                     break label55;\n                  }\n\n                  if (var3 == null) {\n                     break label54;\n                  }\n               } catch (a_ var14) {\n                  throw var14;\n               }\n\n               StringWriter var17 = new StringWriter();\n               PrintWriter var18 = new PrintWriter(var17);\n               var3.printStackTrace(var18);\n               this.e = var17.toString();\n               var18.close();\n            }\n\n            try {\n               var10000 = this;\n               var10001 = var4;\n               if (var10 != 0) {\n                  break label56;\n               }\n\n               this.b = var4;\n            } catch (a_ var13) {\n               throw var13;\n            }\n         }\n\n         try {\n            if (var4 != null) {\n               return;\n            }\n\n            var10000 = this;\n            var10001 = var1;\n         } catch (a_ var12) {\n            throw var12;\n         }\n      }\n\n      var10000.b = var10001;\n   }\n\n   public String a() {\n      return this.c;\n   }\n\n   public String b() {\n      return this.d;\n   }\n\n   public String c() {\n      return this.b;\n   }\n\n   public String d() {\n      return this.e;\n   }\n\n   public String toString() {\n      StringBuilder var1 = new StringBuilder();\n      var1.append(this.e());\n      var1.append(g[4]);\n      var1.append(this.a());\n      var1.append(g[1]);\n      var1.append(g[3]);\n      var1.append(this.c());\n      var1.append(g[2]);\n      return var1.toString();\n   }\n\n   public String e() {\n      return this.a;\n   }\n\n   public Throwable f() {\n      return this.f;\n   }\n\n   static {\n      String[] var10000 = new String[5];\n      char[] var10003 = \"D@\".toCharArray();\n      int var10005 = var10003.length;\n      int var1 = 0;\n      char[] var10004 = var10003;\n      int var2 = var10005;\n      char[] var4;\n      int var10006;\n      char var10007;\n      byte var10008;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 100;\n               break;\n            case 1:\n               var10008 = 104;\n               break;\n            case 2:\n               var10008 = 14;\n               break;\n            case 3:\n               var10008 = 21;\n               break;\n            default:\n               var10008 = 25;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label316: {\n               var10000[0] = (new String(var10003)).intern();\n               var10003 = \"nE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84Ib\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label316;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 100;\n                        break;\n                     case 1:\n                        var10008 = 104;\n                        break;\n                     case 2:\n                        var10008 = 14;\n                        break;\n                     case 3:\n                        var10008 = 21;\n                        break;\n                     default:\n                        var10008 = 25;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[1] = (new String(var10004)).intern();\n            var10003 = \"nE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84Ib\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 100;\n                     break;\n                  case 1:\n                     var10008 = 104;\n                     break;\n                  case 2:\n                     var10008 = 14;\n                     break;\n                  case 3:\n                     var10008 = 21;\n                     break;\n                  default:\n                     var10008 = 25;\n               }\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  label384: {\n                     var10000[2] = (new String(var10003)).intern();\n                     var10003 = \"(\\u0007mtm\\r\\u0007`/\\u0013\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label384;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 100;\n                              break;\n                           case 1:\n                              var10008 = 104;\n                              break;\n                           case 2:\n                              var10008 = 14;\n                              break;\n                           case 3:\n                              var10008 = 21;\n                              break;\n                           default:\n                              var10008 = 25;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[3] = (new String(var10004)).intern();\n                  var10003 = \"Db#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE\\u0004\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[4] = (new String(var10003)).intern();\n                        g = var10000;\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 100;\n                           break;\n                        case 1:\n                           var10008 = 104;\n                           break;\n                        case 2:\n                           var10008 = 14;\n                           break;\n                        case 3:\n                           var10008 = 21;\n                           break;\n                        default:\n                           var10008 = 25;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[4] = (new String(var10004)).intern();\n                           g = var10000;\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 100;\n                     break;\n                  case 1:\n                     var10008 = 104;\n                     break;\n                  case 2:\n                     var10008 = 14;\n                     break;\n                  case 3:\n                     var10008 = 21;\n                     break;\n                  default:\n                     var10008 = 25;\n               }\n            }\n\n            while(true) {\n               while(true) {\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                     var10007 = var10004[var2];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 100;\n                           break;\n                        case 1:\n                           var10008 = 104;\n                           break;\n                        case 2:\n                           var10008 = 14;\n                           break;\n                        case 3:\n                           var10008 = 21;\n                           break;\n                        default:\n                           var10008 = 25;\n                     }\n                  } else {\n                     if (var2 <= var1) {\n                        label492: {\n                           var10000[2] = (new String(var10004)).intern();\n                           var10003 = \"(\\u0007mtm\\r\\u0007`/\\u0013\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label492;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 100;\n                                    break;\n                                 case 1:\n                                    var10008 = 104;\n                                    break;\n                                 case 2:\n                                    var10008 = 14;\n                                    break;\n                                 case 3:\n                                    var10008 = 21;\n                                    break;\n                                 default:\n                                    var10008 = 25;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"Db#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE\\u0004\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[4] = (new String(var10003)).intern();\n                              g = var10000;\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 100;\n                                 break;\n                              case 1:\n                                 var10008 = 104;\n                                 break;\n                              case 2:\n                                 var10008 = 14;\n                                 break;\n                              case 3:\n                                 var10008 = 21;\n                                 break;\n                              default:\n                                 var10008 = 25;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 g = var10000;\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                     var10007 = var10004[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 100;\n                           break;\n                        case 1:\n                           var10008 = 104;\n                           break;\n                        case 2:\n                           var10008 = 14;\n                           break;\n                        case 3:\n                           var10008 = 21;\n                           break;\n                        default:\n                           var10008 = 25;\n                     }\n                  }\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 100;\n               break;\n            case 1:\n               var10008 = 104;\n               break;\n            case 2:\n               var10008 = 14;\n               break;\n            case 3:\n               var10008 = 21;\n               break;\n            default:\n               var10008 = 25;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 100;\n                     break;\n                  case 1:\n                     var10008 = 104;\n                     break;\n                  case 2:\n                     var10008 = 14;\n                     break;\n                  case 3:\n                     var10008 = 21;\n                     break;\n                  default:\n                     var10008 = 25;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label129: {\n                     var10000[0] = (new String(var10004)).intern();\n                     var10003 = \"nE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84Ib\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label129;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 100;\n                              break;\n                           case 1:\n                              var10008 = 104;\n                              break;\n                           case 2:\n                              var10008 = 14;\n                              break;\n                           case 3:\n                              var10008 = 21;\n                              break;\n                           default:\n                              var10008 = 25;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[1] = (new String(var10004)).intern();\n                  var10003 = \"nE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84Ib\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 100;\n                           break;\n                        case 1:\n                           var10008 = 104;\n                           break;\n                        case 2:\n                           var10008 = 14;\n                           break;\n                        case 3:\n                           var10008 = 21;\n                           break;\n                        default:\n                           var10008 = 25;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        label173: {\n                           var10000[2] = (new String(var10003)).intern();\n                           var10003 = \"(\\u0007mtm\\r\\u0007`/\\u0013\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label173;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 100;\n                                    break;\n                                 case 1:\n                                    var10008 = 104;\n                                    break;\n                                 case 2:\n                                    var10008 = 14;\n                                    break;\n                                 case 3:\n                                    var10008 = 21;\n                                    break;\n                                 default:\n                                    var10008 = 25;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"Db#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE\\u0004\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[4] = (new String(var10003)).intern();\n                              g = var10000;\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 100;\n                                 break;\n                              case 1:\n                                 var10008 = 104;\n                                 break;\n                              case 2:\n                                 var10008 = 14;\n                                 break;\n                              case 3:\n                                 var10008 = 21;\n                                 break;\n                              default:\n                                 var10008 = 25;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 g = var10000;\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 100;\n                           break;\n                        case 1:\n                           var10008 = 104;\n                           break;\n                        case 2:\n                           var10008 = 14;\n                           break;\n                        case 3:\n                           var10008 = 21;\n                           break;\n                        default:\n                           var10008 = 25;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                           var10007 = var10004[var2];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 100;\n                                 break;\n                              case 1:\n                                 var10008 = 104;\n                                 break;\n                              case 2:\n                                 var10008 = 14;\n                                 break;\n                              case 3:\n                                 var10008 = 21;\n                                 break;\n                              default:\n                                 var10008 = 25;\n                           }\n                        } else {\n                           if (var2 <= var1) {\n                              label93: {\n                                 var10000[2] = (new String(var10004)).intern();\n                                 var10003 = \"(\\u0007mtm\\r\\u0007`/\\u0013\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= 1) {\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var2 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label93;\n                                    }\n\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var4[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 100;\n                                          break;\n                                       case 1:\n                                          var10008 = 104;\n                                          break;\n                                       case 2:\n                                          var10008 = 14;\n                                          break;\n                                       case 3:\n                                          var10008 = 21;\n                                          break;\n                                       default:\n                                          var10008 = 25;\n                                    }\n\n                                    var4[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var2 == 0) {\n                                       var10006 = var2;\n                                       var4 = var10004;\n                                    } else {\n                                       if (var2 <= var1) {\n                                          break;\n                                       }\n\n                                       var4 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[3] = (new String(var10004)).intern();\n                              var10003 = \"Db#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE#84IE\\u0004\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= 1) {\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              } else {\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= var1) {\n                                    var10000[4] = (new String(var10003)).intern();\n                                    g = var10000;\n                                    return;\n                                 }\n\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              }\n\n                              while(true) {\n                                 var10007 = var4[var10006];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 100;\n                                       break;\n                                    case 1:\n                                       var10008 = 104;\n                                       break;\n                                    case 2:\n                                       var10008 = 14;\n                                       break;\n                                    case 3:\n                                       var10008 = 21;\n                                       break;\n                                    default:\n                                       var10008 = 25;\n                                 }\n\n                                 var4[var10006] = (char)(var10007 ^ var10008);\n                                 ++var1;\n                                 if (var2 == 0) {\n                                    var10006 = var2;\n                                    var4 = var10004;\n                                 } else {\n                                    if (var2 <= var1) {\n                                       var10000[4] = (new String(var10004)).intern();\n                                       g = var10000;\n                                       return;\n                                    }\n\n                                    var4 = var10004;\n                                    var10006 = var1;\n                                 }\n                              }\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 100;\n                                 break;\n                              case 1:\n                                 var10008 = 104;\n                                 break;\n                              case 2:\n                                 var10008 = 14;\n                                 break;\n                              case 3:\n                                 var10008 = 21;\n                                 break;\n                              default:\n                                 var10008 = 25;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 100;\n                     break;\n                  case 1:\n                     var10008 = 104;\n                     break;\n                  case 2:\n                     var10008 = 14;\n                     break;\n                  case 3:\n                     var10008 = 21;\n                     break;\n                  default:\n                     var10008 = 25;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/q.java",
    "content": "public interface q {\n   void a(p var1);\n}\n"
  },
  {
    "path": "testData/obfuscated/r.java",
    "content": "import java.sql.SQLException;\nimport java.util.List;\nimport java.util.logging.Logger;\n\npublic class r {\n   public static final Logger a;\n   @x(\n      a = q.class\n   )\n   private static List<q> b;\n   private static final String[] c;\n\n   public static s a(String var0) {\n      return new s(var0);\n   }\n\n   private static a9 a(p param0) {\n      // $FF: Couldn't be decompiled\n   }\n\n   public static a9 a(Throwable var0) {\n      try {\n         if (var0 instanceof a9) {\n            return (a9)var0;\n         }\n      } catch (a9 var1) {\n         throw var1;\n      }\n\n      try {\n         if (var0 instanceof SQLException) {\n            return a(c[0]).a(var0).a();\n         }\n      } catch (a9 var2) {\n         throw var2;\n      }\n\n      return a(c[1]).a(var0).a();\n   }\n\n   static a9 b(p var0) {\n      return a(var0);\n   }\n\n   static {\n      String[] var10000;\n      int var1;\n      int var2;\n      char[] var10003;\n      char[] var10004;\n      char[] var4;\n      int var10005;\n      int var10006;\n      char var10007;\n      byte var10008;\n      label51: {\n         var10000 = new String[2];\n         var10003 = \"hzE\\u0016\\u0001nt\\u0002\\u0007\\u000bi|\\u000275W\\\\T\\u0007\\u0001kmE\\u000b\\n\".toCharArray();\n         var10005 = var10003.length;\n         var1 = 0;\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= 1) {\n            var4 = var10003;\n            var10006 = var1;\n         } else {\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= var1) {\n               break label51;\n            }\n\n            var4 = var10003;\n            var10006 = var1;\n         }\n\n         while(true) {\n            var10007 = var4[var10006];\n            switch (var1 % 5) {\n               case 0:\n                  var10008 = 27;\n                  break;\n               case 1:\n                  var10008 = 25;\n                  break;\n               case 2:\n                  var10008 = 44;\n                  break;\n               case 3:\n                  var10008 = 100;\n                  break;\n               default:\n                  var10008 = 100;\n            }\n\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n            } else {\n               if (var2 <= var1) {\n                  break;\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n            }\n         }\n      }\n\n      var10000[0] = (new String(var10004)).intern();\n      var10003 = \"hzE\\u0016\\u0001nt\\u0002\\u0007\\u000bi|\\u00021\\n~a\\\\\\u0001\\u0007o|H!\\u001cx|\\\\\\u0010\\rtw\".toCharArray();\n      var10005 = var10003.length;\n      var1 = 0;\n      var10004 = var10003;\n      var2 = var10005;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            var10000[1] = (new String(var10003)).intern();\n            c = var10000;\n            a = Logger.getLogger(r.class.getName());\n            return;\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n      }\n\n      while(true) {\n         var10007 = var4[var10006];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 27;\n               break;\n            case 1:\n               var10008 = 25;\n               break;\n            case 2:\n               var10008 = 44;\n               break;\n            case 3:\n               var10008 = 100;\n               break;\n            default:\n               var10008 = 100;\n         }\n\n         var4[var10006] = (char)(var10007 ^ var10008);\n         ++var1;\n         if (var2 == 0) {\n            var10006 = var2;\n            var4 = var10004;\n         } else {\n            if (var2 <= var1) {\n               var10000[1] = (new String(var10004)).intern();\n               c = var10000;\n               a = Logger.getLogger(r.class.getName());\n               return;\n            }\n\n            var4 = var10004;\n            var10006 = var1;\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/s.java",
    "content": "import java.util.LinkedHashMap;\nimport java.util.Map;\n\npublic class s {\n   private final String a;\n   private Map<String, String> b = new LinkedHashMap();\n   private Throwable c;\n   public static int d;\n   private static final String[] e;\n\n   public s(String var1) {\n      this.a = var1;\n   }\n\n   public s a(Throwable var1) {\n      this.c = var1;\n      return this;\n   }\n\n   public s a(String var1, Object var2) {\n      try {\n         if (var2 != null) {\n            this.b.put(var1, var2.toString());\n         }\n\n         return this;\n      } catch (a_ var3) {\n         throw var3;\n      }\n   }\n\n   public a9 a() {\n      return r.b(this.b());\n   }\n\n   protected p b() {\n      // $FF: Couldn't be decompiled\n   }\n\n   protected static String b(Throwable var0) {\n      String var1 = \"-\";\n      StackTraceElement[] var2;\n      if (var0 != null) {\n         var2 = var0.getStackTrace();\n         if (var2.length > 0) {\n            var1 = var2[0].getFileName() + \":\" + var2[0].getLineNumber() + \"[\" + var2[0].getClassName() + \".\" + var2[0].getMethodName() + \"]\";\n         }\n      } else {\n         var2 = Thread.currentThread().getStackTrace();\n         if (var2.length > 5) {\n            var1 = var2[5].getFileName() + \":\" + var2[5].getLineNumber() + \"[\" + var2[5].getClassName() + \".\" + var2[5].getMethodName() + \"]\";\n         }\n      }\n\n      return var1;\n   }\n\n   static {\n      String[] var10000 = new String[5];\n      char[] var10003 = \"][BX\\u0014\\u0014\\u000bsI\\u0018\\u001fA'\".toCharArray();\n      int var10005 = var10003.length;\n      int var1 = 0;\n      char[] var10004 = var10003;\n      int var2 = var10005;\n      char[] var4;\n      int var10006;\n      char var10007;\n      byte var10008;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 113;\n               break;\n            case 1:\n               var10008 = 123;\n               break;\n            case 2:\n               var10008 = 7;\n               break;\n            case 3:\n               var10008 = 32;\n               break;\n            default:\n               var10008 = 119;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label316: {\n               var10000[0] = (new String(var10003)).intern();\n               var10003 = \"\\u001e\\tnG\\u001e\\u001f\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label316;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 113;\n                        break;\n                     case 1:\n                        var10008 = 123;\n                        break;\n                     case 2:\n                        var10008 = 7;\n                        break;\n                     case 3:\n                        var10008 = 32;\n                        break;\n                     default:\n                        var10008 = 119;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[1] = (new String(var10004)).intern();\n            var10003 = \"K[\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 113;\n                     break;\n                  case 1:\n                     var10008 = 123;\n                     break;\n                  case 2:\n                     var10008 = 7;\n                     break;\n                  case 3:\n                     var10008 = 32;\n                     break;\n                  default:\n                     var10008 = 119;\n               }\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  label384: {\n                     var10000[2] = (new String(var10003)).intern();\n                     var10003 = \"QF'\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label384;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 113;\n                              break;\n                           case 1:\n                              var10008 = 123;\n                              break;\n                           case 2:\n                              var10008 = 7;\n                              break;\n                           case 3:\n                              var10008 = 32;\n                              break;\n                           default:\n                              var10008 = 119;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[3] = (new String(var10004)).intern();\n                  var10003 = \"][\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[4] = (new String(var10003)).intern();\n                        e = var10000;\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 113;\n                           break;\n                        case 1:\n                           var10008 = 123;\n                           break;\n                        case 2:\n                           var10008 = 7;\n                           break;\n                        case 3:\n                           var10008 = 32;\n                           break;\n                        default:\n                           var10008 = 119;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[4] = (new String(var10004)).intern();\n                           e = var10000;\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n               var10007 = var10003[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 113;\n                     break;\n                  case 1:\n                     var10008 = 123;\n                     break;\n                  case 2:\n                     var10008 = 7;\n                     break;\n                  case 3:\n                     var10008 = 32;\n                     break;\n                  default:\n                     var10008 = 119;\n               }\n            }\n\n            while(true) {\n               while(true) {\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                     var10007 = var10004[var2];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 113;\n                           break;\n                        case 1:\n                           var10008 = 123;\n                           break;\n                        case 2:\n                           var10008 = 7;\n                           break;\n                        case 3:\n                           var10008 = 32;\n                           break;\n                        default:\n                           var10008 = 119;\n                     }\n                  } else {\n                     if (var2 <= var1) {\n                        label492: {\n                           var10000[2] = (new String(var10004)).intern();\n                           var10003 = \"QF'\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label492;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 113;\n                                    break;\n                                 case 1:\n                                    var10008 = 123;\n                                    break;\n                                 case 2:\n                                    var10008 = 7;\n                                    break;\n                                 case 3:\n                                    var10008 = 32;\n                                    break;\n                                 default:\n                                    var10008 = 119;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"][\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[4] = (new String(var10003)).intern();\n                              e = var10000;\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 113;\n                                 break;\n                              case 1:\n                                 var10008 = 123;\n                                 break;\n                              case 2:\n                                 var10008 = 7;\n                                 break;\n                              case 3:\n                                 var10008 = 32;\n                                 break;\n                              default:\n                                 var10008 = 119;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 e = var10000;\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                     var10007 = var10004[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 113;\n                           break;\n                        case 1:\n                           var10008 = 123;\n                           break;\n                        case 2:\n                           var10008 = 7;\n                           break;\n                        case 3:\n                           var10008 = 32;\n                           break;\n                        default:\n                           var10008 = 119;\n                     }\n                  }\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 113;\n               break;\n            case 1:\n               var10008 = 123;\n               break;\n            case 2:\n               var10008 = 7;\n               break;\n            case 3:\n               var10008 = 32;\n               break;\n            default:\n               var10008 = 119;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 113;\n                     break;\n                  case 1:\n                     var10008 = 123;\n                     break;\n                  case 2:\n                     var10008 = 7;\n                     break;\n                  case 3:\n                     var10008 = 32;\n                     break;\n                  default:\n                     var10008 = 119;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label129: {\n                     var10000[0] = (new String(var10004)).intern();\n                     var10003 = \"\\u001e\\tnG\\u001e\\u001f\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label129;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 113;\n                              break;\n                           case 1:\n                              var10008 = 123;\n                              break;\n                           case 2:\n                              var10008 = 7;\n                              break;\n                           case 3:\n                              var10008 = 32;\n                              break;\n                           default:\n                              var10008 = 119;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[1] = (new String(var10004)).intern();\n                  var10003 = \"K[\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 113;\n                           break;\n                        case 1:\n                           var10008 = 123;\n                           break;\n                        case 2:\n                           var10008 = 7;\n                           break;\n                        case 3:\n                           var10008 = 32;\n                           break;\n                        default:\n                           var10008 = 119;\n                     }\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        label173: {\n                           var10000[2] = (new String(var10003)).intern();\n                           var10003 = \"QF'\".toCharArray();\n                           var10005 = var10003.length;\n                           var1 = 0;\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= 1) {\n                              var4 = var10003;\n                              var10006 = var1;\n                           } else {\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= var1) {\n                                 break label173;\n                              }\n\n                              var4 = var10003;\n                              var10006 = var1;\n                           }\n\n                           while(true) {\n                              var10007 = var4[var10006];\n                              switch (var1 % 5) {\n                                 case 0:\n                                    var10008 = 113;\n                                    break;\n                                 case 1:\n                                    var10008 = 123;\n                                    break;\n                                 case 2:\n                                    var10008 = 7;\n                                    break;\n                                 case 3:\n                                    var10008 = 32;\n                                    break;\n                                 default:\n                                    var10008 = 119;\n                              }\n\n                              var4[var10006] = (char)(var10007 ^ var10008);\n                              ++var1;\n                              if (var2 == 0) {\n                                 var10006 = var2;\n                                 var4 = var10004;\n                              } else {\n                                 if (var2 <= var1) {\n                                    break;\n                                 }\n\n                                 var4 = var10004;\n                                 var10006 = var1;\n                              }\n                           }\n                        }\n\n                        var10000[3] = (new String(var10004)).intern();\n                        var10003 = \"][\".toCharArray();\n                        var10005 = var10003.length;\n                        var1 = 0;\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= 1) {\n                           var4 = var10003;\n                           var10006 = var1;\n                        } else {\n                           var10004 = var10003;\n                           var2 = var10005;\n                           if (var10005 <= var1) {\n                              var10000[4] = (new String(var10003)).intern();\n                              e = var10000;\n                              return;\n                           }\n\n                           var4 = var10003;\n                           var10006 = var1;\n                        }\n\n                        while(true) {\n                           var10007 = var4[var10006];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 113;\n                                 break;\n                              case 1:\n                                 var10008 = 123;\n                                 break;\n                              case 2:\n                                 var10008 = 7;\n                                 break;\n                              case 3:\n                                 var10008 = 32;\n                                 break;\n                              default:\n                                 var10008 = 119;\n                           }\n\n                           var4[var10006] = (char)(var10007 ^ var10008);\n                           ++var1;\n                           if (var2 == 0) {\n                              var10006 = var2;\n                              var4 = var10004;\n                           } else {\n                              if (var2 <= var1) {\n                                 var10000[4] = (new String(var10004)).intern();\n                                 e = var10000;\n                                 return;\n                              }\n\n                              var4 = var10004;\n                              var10006 = var1;\n                           }\n                        }\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                     var10007 = var10003[var1];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 113;\n                           break;\n                        case 1:\n                           var10008 = 123;\n                           break;\n                        case 2:\n                           var10008 = 7;\n                           break;\n                        case 3:\n                           var10008 = 32;\n                           break;\n                        default:\n                           var10008 = 119;\n                     }\n                  }\n\n                  while(true) {\n                     while(true) {\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                           var10007 = var10004[var2];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 113;\n                                 break;\n                              case 1:\n                                 var10008 = 123;\n                                 break;\n                              case 2:\n                                 var10008 = 7;\n                                 break;\n                              case 3:\n                                 var10008 = 32;\n                                 break;\n                              default:\n                                 var10008 = 119;\n                           }\n                        } else {\n                           if (var2 <= var1) {\n                              label93: {\n                                 var10000[2] = (new String(var10004)).intern();\n                                 var10003 = \"QF'\".toCharArray();\n                                 var10005 = var10003.length;\n                                 var1 = 0;\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= 1) {\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 } else {\n                                    var10004 = var10003;\n                                    var2 = var10005;\n                                    if (var10005 <= var1) {\n                                       break label93;\n                                    }\n\n                                    var4 = var10003;\n                                    var10006 = var1;\n                                 }\n\n                                 while(true) {\n                                    var10007 = var4[var10006];\n                                    switch (var1 % 5) {\n                                       case 0:\n                                          var10008 = 113;\n                                          break;\n                                       case 1:\n                                          var10008 = 123;\n                                          break;\n                                       case 2:\n                                          var10008 = 7;\n                                          break;\n                                       case 3:\n                                          var10008 = 32;\n                                          break;\n                                       default:\n                                          var10008 = 119;\n                                    }\n\n                                    var4[var10006] = (char)(var10007 ^ var10008);\n                                    ++var1;\n                                    if (var2 == 0) {\n                                       var10006 = var2;\n                                       var4 = var10004;\n                                    } else {\n                                       if (var2 <= var1) {\n                                          break;\n                                       }\n\n                                       var4 = var10004;\n                                       var10006 = var1;\n                                    }\n                                 }\n                              }\n\n                              var10000[3] = (new String(var10004)).intern();\n                              var10003 = \"][\".toCharArray();\n                              var10005 = var10003.length;\n                              var1 = 0;\n                              var10004 = var10003;\n                              var2 = var10005;\n                              if (var10005 <= 1) {\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              } else {\n                                 var10004 = var10003;\n                                 var2 = var10005;\n                                 if (var10005 <= var1) {\n                                    var10000[4] = (new String(var10003)).intern();\n                                    e = var10000;\n                                    return;\n                                 }\n\n                                 var4 = var10003;\n                                 var10006 = var1;\n                              }\n\n                              while(true) {\n                                 var10007 = var4[var10006];\n                                 switch (var1 % 5) {\n                                    case 0:\n                                       var10008 = 113;\n                                       break;\n                                    case 1:\n                                       var10008 = 123;\n                                       break;\n                                    case 2:\n                                       var10008 = 7;\n                                       break;\n                                    case 3:\n                                       var10008 = 32;\n                                       break;\n                                    default:\n                                       var10008 = 119;\n                                 }\n\n                                 var4[var10006] = (char)(var10007 ^ var10008);\n                                 ++var1;\n                                 if (var2 == 0) {\n                                    var10006 = var2;\n                                    var4 = var10004;\n                                 } else {\n                                    if (var2 <= var1) {\n                                       var10000[4] = (new String(var10004)).intern();\n                                       e = var10000;\n                                       return;\n                                    }\n\n                                    var4 = var10004;\n                                    var10006 = var1;\n                                 }\n                              }\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                           var10007 = var10004[var1];\n                           switch (var1 % 5) {\n                              case 0:\n                                 var10008 = 113;\n                                 break;\n                              case 1:\n                                 var10008 = 123;\n                                 break;\n                              case 2:\n                                 var10008 = 7;\n                                 break;\n                              case 3:\n                                 var10008 = 32;\n                                 break;\n                              default:\n                                 var10008 = 119;\n                           }\n                        }\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 113;\n                     break;\n                  case 1:\n                     var10008 = 123;\n                     break;\n                  case 2:\n                     var10008 = 7;\n                     break;\n                  case 3:\n                     var10008 = 32;\n                     break;\n                  default:\n                     var10008 = 119;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/u.java",
    "content": "public interface u {\n   void a(Class<?> var1) throws Exception;\n\n   void a() throws Exception;\n}\n"
  },
  {
    "path": "testData/obfuscated/v.java",
    "content": "import java.lang.reflect.Field;\nimport java.util.List;\nimport java.util.logging.Level;\n\npublic class v {\n   private static final String[] a;\n\n   public static Object a(Object var0) {\n      try {\n         if (var0 != null) {\n            a(var0, var0.getClass());\n         }\n\n         return var0;\n      } catch (IllegalArgumentException var1) {\n         throw var1;\n      }\n   }\n\n   private static void a(Object param0, Class<?> param1) {\n      // $FF: Couldn't be decompiled\n   }\n\n   private static void a(Field var0, Object var1) {\n      int var3 = y.d;\n\n      Throwable var10000;\n      boolean var10001;\n      label43: {\n         try {\n            if (var3 != 0) {\n               return;\n            }\n\n            if (!List.class.isAssignableFrom(var0.getType())) {\n               break label43;\n            }\n         } catch (Throwable var7) {\n            throw var7;\n         }\n\n         Throwable var2;\n         try {\n            var0.set(var1, t.b(((x)var0.getAnnotation(x.class)).a()));\n            return;\n         } catch (Throwable var5) {\n            var2 = var5;\n         }\n\n         try {\n            t.a.log(Level.WARNING, var1.getClass() + \".\" + var0.getName() + a[1] + var2.getMessage(), var2);\n            if (var3 == 0) {\n               return;\n            }\n         } catch (Throwable var6) {\n            var10000 = var6;\n            var10001 = false;\n            throw var10000;\n         }\n      }\n\n      try {\n         t.a.warning(var1.getClass() + \".\" + var0.getName() + a[2]);\n      } catch (Throwable var4) {\n         var10000 = var4;\n         var10001 = false;\n         throw var10000;\n      }\n   }\n\n   private static void b(Field var0, Object var1) {\n      try {\n         var0.set(var1, t.a(var0.getType()));\n      } catch (Throwable var3) {\n         t.a.log(Level.WARNING, var1.getClass() + \".\" + var0.getName() + a[0] + var3.getMessage(), var3);\n      }\n\n   }\n\n   public <I> I a(Class<I> var1) {\n      try {\n         return a(var1.newInstance());\n      } catch (Throwable var3) {\n         throw new IllegalArgumentException(var3);\n      }\n   }\n\n   static {\n      String[] var10000 = new String[3];\n      char[] var10003 = \"]\\u0007\".toCharArray();\n      int var10005 = var10003.length;\n      int var1 = 0;\n      char[] var10004 = var10003;\n      int var2 = var10005;\n      char[] var4;\n      int var10006;\n      char var10007;\n      byte var10008;\n      if (var10005 <= 1) {\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 103;\n               break;\n            case 1:\n               var10008 = 39;\n               break;\n            case 2:\n               var10008 = 44;\n               break;\n            case 3:\n               var10008 = 46;\n               break;\n            default:\n               var10008 = 93;\n         }\n      } else {\n         var10004 = var10003;\n         var2 = var10005;\n         if (var10005 <= var1) {\n            label127: {\n               var10000[0] = (new String(var10003)).intern();\n               var10003 = \"]\\u0007\".toCharArray();\n               var10005 = var10003.length;\n               var1 = 0;\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= 1) {\n                  var4 = var10003;\n                  var10006 = var1;\n               } else {\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= var1) {\n                     break label127;\n                  }\n\n                  var4 = var10003;\n                  var10006 = var1;\n               }\n\n               while(true) {\n                  var10007 = var4[var10006];\n                  switch (var1 % 5) {\n                     case 0:\n                        var10008 = 103;\n                        break;\n                     case 1:\n                        var10008 = 39;\n                        break;\n                     case 2:\n                        var10008 = 44;\n                        break;\n                     case 3:\n                        var10008 = 46;\n                        break;\n                     default:\n                        var10008 = 93;\n                  }\n\n                  var4[var10006] = (char)(var10007 ^ var10008);\n                  ++var1;\n                  if (var2 == 0) {\n                     var10006 = var2;\n                     var4 = var10004;\n                  } else {\n                     if (var2 <= var1) {\n                        break;\n                     }\n\n                     var4 = var10004;\n                     var10006 = var1;\n                  }\n               }\n            }\n\n            var10000[1] = (new String(var10004)).intern();\n            var10003 = \"]\\u0007lg3\\rBOZ\\u0011\\u000eTX\\u000e/\\u0002VYG/\\u0002C\\fO}\\rFZOs\\u0012SEBs+N_Za\\\"\\u0019\\fO.GAEK1\\u0003\\u0007XW-\\u0002\".toCharArray();\n            var10005 = var10003.length;\n            var1 = 0;\n            var10004 = var10003;\n            var2 = var10005;\n            if (var10005 <= 1) {\n               var4 = var10003;\n               var10006 = var1;\n            } else {\n               var10004 = var10003;\n               var2 = var10005;\n               if (var10005 <= var1) {\n                  var10000[2] = (new String(var10003)).intern();\n                  a = var10000;\n                  return;\n               }\n\n               var4 = var10003;\n               var10006 = var1;\n            }\n\n            while(true) {\n               var10007 = var4[var10006];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 103;\n                     break;\n                  case 1:\n                     var10008 = 39;\n                     break;\n                  case 2:\n                     var10008 = 44;\n                     break;\n                  case 3:\n                     var10008 = 46;\n                     break;\n                  default:\n                     var10008 = 93;\n               }\n\n               var4[var10006] = (char)(var10007 ^ var10008);\n               ++var1;\n               if (var2 == 0) {\n                  var10006 = var2;\n                  var4 = var10004;\n               } else {\n                  if (var2 <= var1) {\n                     var10000[2] = (new String(var10004)).intern();\n                     a = var10000;\n                     return;\n                  }\n\n                  var4 = var10004;\n                  var10006 = var1;\n               }\n            }\n         }\n\n         var4 = var10003;\n         var10006 = var1;\n         var10007 = var10003[var1];\n         switch (var1 % 5) {\n            case 0:\n               var10008 = 103;\n               break;\n            case 1:\n               var10008 = 39;\n               break;\n            case 2:\n               var10008 = 44;\n               break;\n            case 3:\n               var10008 = 46;\n               break;\n            default:\n               var10008 = 93;\n         }\n      }\n\n      while(true) {\n         while(true) {\n            var4[var10006] = (char)(var10007 ^ var10008);\n            ++var1;\n            if (var2 == 0) {\n               var10006 = var2;\n               var4 = var10004;\n               var10007 = var10004[var2];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 103;\n                     break;\n                  case 1:\n                     var10008 = 39;\n                     break;\n                  case 2:\n                     var10008 = 44;\n                     break;\n                  case 3:\n                     var10008 = 46;\n                     break;\n                  default:\n                     var10008 = 93;\n               }\n            } else {\n               if (var2 <= var1) {\n                  label65: {\n                     var10000[0] = (new String(var10004)).intern();\n                     var10003 = \"]\\u0007\".toCharArray();\n                     var10005 = var10003.length;\n                     var1 = 0;\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= 1) {\n                        var4 = var10003;\n                        var10006 = var1;\n                     } else {\n                        var10004 = var10003;\n                        var2 = var10005;\n                        if (var10005 <= var1) {\n                           break label65;\n                        }\n\n                        var4 = var10003;\n                        var10006 = var1;\n                     }\n\n                     while(true) {\n                        var10007 = var4[var10006];\n                        switch (var1 % 5) {\n                           case 0:\n                              var10008 = 103;\n                              break;\n                           case 1:\n                              var10008 = 39;\n                              break;\n                           case 2:\n                              var10008 = 44;\n                              break;\n                           case 3:\n                              var10008 = 46;\n                              break;\n                           default:\n                              var10008 = 93;\n                        }\n\n                        var4[var10006] = (char)(var10007 ^ var10008);\n                        ++var1;\n                        if (var2 == 0) {\n                           var10006 = var2;\n                           var4 = var10004;\n                        } else {\n                           if (var2 <= var1) {\n                              break;\n                           }\n\n                           var4 = var10004;\n                           var10006 = var1;\n                        }\n                     }\n                  }\n\n                  var10000[1] = (new String(var10004)).intern();\n                  var10003 = \"]\\u0007lg3\\rBOZ\\u0011\\u000eTX\\u000e/\\u0002VYG/\\u0002C\\fO}\\rFZOs\\u0012SEBs+N_Za\\\"\\u0019\\fO.GAEK1\\u0003\\u0007XW-\\u0002\".toCharArray();\n                  var10005 = var10003.length;\n                  var1 = 0;\n                  var10004 = var10003;\n                  var2 = var10005;\n                  if (var10005 <= 1) {\n                     var4 = var10003;\n                     var10006 = var1;\n                  } else {\n                     var10004 = var10003;\n                     var2 = var10005;\n                     if (var10005 <= var1) {\n                        var10000[2] = (new String(var10003)).intern();\n                        a = var10000;\n                        return;\n                     }\n\n                     var4 = var10003;\n                     var10006 = var1;\n                  }\n\n                  while(true) {\n                     var10007 = var4[var10006];\n                     switch (var1 % 5) {\n                        case 0:\n                           var10008 = 103;\n                           break;\n                        case 1:\n                           var10008 = 39;\n                           break;\n                        case 2:\n                           var10008 = 44;\n                           break;\n                        case 3:\n                           var10008 = 46;\n                           break;\n                        default:\n                           var10008 = 93;\n                     }\n\n                     var4[var10006] = (char)(var10007 ^ var10008);\n                     ++var1;\n                     if (var2 == 0) {\n                        var10006 = var2;\n                        var4 = var10004;\n                     } else {\n                        if (var2 <= var1) {\n                           var10000[2] = (new String(var10004)).intern();\n                           a = var10000;\n                           return;\n                        }\n\n                        var4 = var10004;\n                        var10006 = var1;\n                     }\n                  }\n               }\n\n               var4 = var10004;\n               var10006 = var1;\n               var10007 = var10004[var1];\n               switch (var1 % 5) {\n                  case 0:\n                     var10008 = 103;\n                     break;\n                  case 1:\n                     var10008 = 39;\n                     break;\n                  case 2:\n                     var10008 = 44;\n                     break;\n                  case 3:\n                     var10008 = 46;\n                     break;\n                  default:\n                     var10008 = 93;\n               }\n            }\n         }\n      }\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/w.java",
    "content": "import java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD})\npublic @interface w {\n}\n"
  },
  {
    "path": "testData/obfuscated/x.java",
    "content": "import java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.FIELD})\npublic @interface x {\n   Class<?> a();\n}\n"
  },
  {
    "path": "testData/obfuscated/y.java",
    "content": "public class y<P> {\n   private P a;\n   private Class<P> b;\n   private boolean c;\n   public static int d;\n\n   private y(Class<P> var1) {\n      this.b = var1;\n   }\n\n   public static <P> y<P> a(Class<P> var0) {\n      return new y(var0);\n   }\n\n   public P a() {\n      // $FF: Couldn't be decompiled\n   }\n}\n"
  },
  {
    "path": "testData/obfuscated/z.java",
    "content": "import java.util.List;\n\npublic class z<P> {\n   private List<P> a;\n   private Class<P> b;\n\n   private z(Class<P> var1) {\n      this.b = var1;\n   }\n\n   public static <P> z<P> a(Class<P> var0) {\n      return new z(var0);\n   }\n\n   public List<P> a() {\n      // $FF: Couldn't be decompiled\n   }\n\n   public List<P> b() {\n      return t.b(this.b);\n   }\n}\n"
  },
  {
    "path": "testData/results/ArrayNestedTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\npublic class ArrayNestedTypeAnnotations {\n   @A Z.Y.X.W[] w1;\n   Z.@B Y.X.W[] @E [] w2;\n   Z.Y.@C X.W @F [] @A [] @B [] w3;\n   Z.Y.X.@D W @D [] w4;\n   @A Z.@B Y.@C X.@D W[][] w5;\n   @L Z.Y.X.@L W[] @L [] w6;\n}\n\n"
  },
  {
    "path": "testData/results/ArrayTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\npublic class ArrayTypeAnnotations implements ParentInterface {\n   @A String[] s1 = new String[0];\n   String @B [] s2 = new String[0];\n   String @C [][] s3 = new String[0][0];\n   String[] @D [] s4 = new String[0][0];\n\n   @A String[] s5() {\n      return null;// 8\n   }\n\n   String @B [] s6() {\n      return null;// 9\n   }\n\n   @A String @B [] @C [] @D [] s7() {\n      return null;// 10\n   }\n\n   @L String @L [][] @L [] s8() {\n      return null;// 11\n   }\n}\n\nclass 'typeAnnotations/ArrayTypeAnnotations' {\n   method 's5 ()[Ljava/lang/String;' {\n      0      9\n      1      9\n   }\n\n   method 's6 ()[Ljava/lang/String;' {\n      0      13\n      1      13\n   }\n\n   method 's7 ()[[[Ljava/lang/String;' {\n      0      17\n      1      17\n   }\n\n   method 's8 ()[[[Ljava/lang/String;' {\n      0      21\n      1      21\n   }\n}\n\nLines mapping:\n8 <-> 10\n9 <-> 14\n10 <-> 18\n11 <-> 22\n"
  },
  {
    "path": "testData/results/ClassNonSealed.dec",
    "content": "package sealed;\n\nnon-sealed class ClassNonSealed extends RootWithClassOuter implements RootWithInterfaceInnerAndOuter {\n}\n\n"
  },
  {
    "path": "testData/results/ClassNonSealedExtendsImplements.dec",
    "content": "package sealed;\n\nnon-sealed class ClassNonSealedExtendsImplements extends RootWithClassOuter implements RootWithInterfaceOuter {\n}\n\n"
  },
  {
    "path": "testData/results/ClassSuperTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\npublic class ClassSuperTypeAnnotations extends @L @A Foo implements @B Bar, @B BarGeneric<@F String, @L @A String @B []> {\n}\n\n"
  },
  {
    "path": "testData/results/EnumWithOverride.dec",
    "content": "package sealed;\n\nenum EnumWithOverride {\n   FOO {\n   };\n}\n\n"
  },
  {
    "path": "testData/results/GenericArrayNestedTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\npublic class GenericArrayNestedTypeAnnotations {\n   @A V.U<String>.T<Boolean, Integer, Float> @A [] t1;\n   V.@B U<String>.T<Boolean, Integer, Float>[] t2;\n   V.U<String>.@C T<Boolean, Integer, Float> @B [] @D [] t3;\n   V.U<@D String>.T<Boolean, Integer, Float> @F [] t4;\n   @B V.@A U<@A String>.@A T<@E Boolean, @F Integer, Float>[] t5;\n   @L V.U<String>.T<Boolean, Integer, Float>[][] t6;\n}\n\n"
  },
  {
    "path": "testData/results/GenericArrayTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class GenericArrayTypeAnnotations {\n   @A List<Comparable<Object[][][]>> l1;\n   List<@B Comparable<Object[][][]>> l2;\n   List<Comparable<@F Object[][][]>> l3;\n   List<Comparable<Object @C [][][]>> l4;\n   List<Comparable<Object[] @D [][]>> l5;\n   List<Comparable<Object[][] @E []>> l6;\n   @A List<@B Comparable<@F Object @C [] @D [] @E []>> l7;\n   @A List<@A Comparable<@A Object @A [] @A [] @A []>> l8;\n   Map<Object @A @B [][], List<@E Map<Object @C [] @D [], @F Object>>> m1;\n   @B @A Map<@A Object @F [] @E [], @D List<@F @A Map<@B @D Object @A @D [] @D [], @A @B @D Object>>> m2;\n   @A @B Map<@B Map<@B Map<@B List<@F ? extends @A String @C [] @D [] @E @F []>, @A List<@B ? super @D String @E @A @F [] @F []>>, @A List<@C ?>>, @D List<@A Map<@E Object @F [] @D [], List<@D Object>> @A []> @B [] @C []> @D [] m3;\n\n   @A Map<@A Object, @B List<@C Object @D [] @E [] @F []>> m12() {\n      return null;// 20\n   }\n\n   @A List<@B Object> @E [] @F [] l1() {\n      return null;// 24\n   }\n\n   @A List<@B Object @C [] @D []> @E [] @F [] l2() {\n      return null;// 28\n   }\n\n   @L List<Object[][]>[] @L [] l3() {\n      return null;// 32\n   }\n}\n\nclass 'typeAnnotations/GenericArrayTypeAnnotations' {\n   method 'm12 ()Ljava/util/Map;' {\n      0      19\n      1      19\n   }\n\n   method 'l1 ()[[Ljava/util/List;' {\n      0      23\n      1      23\n   }\n\n   method 'l2 ()[[Ljava/util/List;' {\n      0      27\n      1      27\n   }\n\n   method 'l3 ()[[Ljava/util/List;' {\n      0      31\n      1      31\n   }\n}\n\nLines mapping:\n20 <-> 20\n24 <-> 24\n28 <-> 28\n32 <-> 32\n"
  },
  {
    "path": "testData/results/GenericNestedTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\npublic class GenericNestedTypeAnnotations {\n   @A V.U<String>.T<Boolean, Integer, Float> t1;\n   V.@B U<String>.T<Boolean, Integer, Float> t2;\n   V.U<String>.@C T<Boolean, Integer, Float> t3;\n   V.U<@D String>.T<Boolean, Integer, Float> t4;\n   V.U<String>.T<@E Boolean, @F Integer, Float> t5;\n   @L V.U<String>.T<@L Boolean, @F Integer, Float> t6;\n}\n\n"
  },
  {
    "path": "testData/results/GenericTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class GenericTypeAnnotations {\n   @A Map<? extends String, List<Object>> m1;\n   Map<@B ? extends String, List<Object>> m2;\n   Map<? extends @C String, List<Object>> m3;\n   Map<? extends String, @D List<Object>> m4;\n   Map<? extends String, List<@E Object>> m5;\n   @A @B Map<? extends String, List<Object>> m6;\n   Map<@A @B ? extends String, List<Object>> m7;\n   Map<? extends @C @D String, List<Object>> m8;\n   Map<? extends String, @D @E List<Object>> m9;\n   Map<? extends String, List<@E @F Object>> m10;\n   @A Map<@B ? extends @C String, @L @D List<@E Object>> m11;\n   @L Map<? extends String, List<Object>> m13;\n\n   @A Map<@A Object, @B List<@C Object>> m12() {\n      return null;// 18\n   }\n\n   @A @B List<@C Object> l1() {\n      return null;// 19\n   }\n}\n\nclass 'typeAnnotations/GenericTypeAnnotations' {\n   method 'm12 ()Ljava/util/Map;' {\n      0      20\n      1      20\n   }\n\n   method 'l1 ()Ljava/util/List;' {\n      0      24\n      1      24\n   }\n}\n\nLines mapping:\n18 <-> 21\n19 <-> 25\n"
  },
  {
    "path": "testData/results/Integer.dec",
    "content": "package java.lang;\n\nimport sun.misc.VM;\n\npublic final class Integer extends Number implements Comparable<Integer> {\n   public static final int MIN_VALUE = -2147483648;\n   public static final int MAX_VALUE = 2147483647;\n   public static final Class<Integer> TYPE = Class.getPrimitiveClass(\"int\");\n   static final char[] digits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};\n   static final char[] DigitTens = new char[]{'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9'};\n   static final char[] DigitOnes = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};\n   static final int[] sizeTable = new int[]{9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, MAX_VALUE};\n   private final int value;\n   public static final int SIZE = 32;\n   public static final int BYTES = 4;\n   private static final long serialVersionUID = 1360826667806852920L;\n\n   public static String toString(int var0, int var1) {\n      if (var1 < 2 || var1 > 36) {// 131\n         var1 = 10;// 132\n      }\n\n      if (var1 == 10) {// 135\n         return toString(var0);// 136\n      } else {\n         char[] var2 = new char[33];// 139\n         boolean var3 = var0 < 0;// 140\n         int var4 = 32;// 141\n         if (!var3) {// 143\n            var0 = -var0;// 144\n         }\n\n         while(var0 <= -var1) {// 147\n            var2[var4--] = digits[-(var0 % var1)];// 148\n            var0 /= var1;// 149\n         }\n\n         var2[var4] = digits[-var0];// 151\n         if (var3) {// 153\n            --var4;// 154\n            var2[var4] = '-';\n         }\n\n         return new String(var2, var4, 33 - var4);// 157\n      }\n   }\n\n   public static String toUnsignedString(int var0, int var1) {\n      return Long.toUnsignedString(toUnsignedLong(var0), var1);// 187\n   }\n\n   public static String toHexString(int var0) {\n      return toUnsignedString0(var0, 4);// 233\n   }\n\n   public static String toOctalString(int var0) {\n      return toUnsignedString0(var0, 3);// 271\n   }\n\n   public static String toBinaryString(int var0) {\n      return toUnsignedString0(var0, 1);// 303\n   }\n\n   private static String toUnsignedString0(int var0, int var1) {\n      int var2 = 32 - numberOfLeadingZeros(var0);// 311\n      int var3 = Math.max((var2 + (var1 - 1)) / var1, 1);// 312\n      char[] var4 = new char[var3];// 313\n      formatUnsignedInt(var0, var1, var4, 0, var3);// 315\n      return new String(var4, true);// 318\n   }\n\n   static int formatUnsignedInt(int var0, int var1, char[] var2, int var3, int var4) {\n      int var5 = var4;// 331\n      int var6 = 1 << var1;// 332\n      int var7 = var6 - 1;// 333\n\n      do {\n         --var5;// 335\n         var2[var3 + var5] = digits[var0 & var7];\n         var0 >>>= var1;// 336\n      } while(var0 != 0 && var5 > 0);// 337\n\n      return var5;// 339\n   }\n\n   public static String toString(int var0) {\n      if (var0 == MIN_VALUE) {// 398\n         return \"-2147483648\";// 399\n      } else {\n         int var1 = var0 < 0 ? stringSize(-var0) + 1 : stringSize(var0);// 400\n         char[] var2 = new char[var1];// 401\n         getChars(var0, var1, var2);// 402\n         return new String(var2, true);// 403\n      }\n   }\n\n   public static String toUnsignedString(int var0) {\n      return Long.toString(toUnsignedLong(var0));// 421\n   }\n\n   static void getChars(int var0, int var1, char[] var2) {\n      int var5 = var1;// 435\n      byte var6 = 0;// 436\n      if (var0 < 0) {// 438\n         var6 = 45;// 439\n         var0 = -var0;// 440\n      }\n\n      int var3;\n      int var4;\n      while(var0 >= 65536) {// 444\n         var3 = var0 / 100;// 445\n         var4 = var0 - ((var3 << 6) + (var3 << 5) + (var3 << 2));// 447\n         var0 = var3;// 448\n         --var5;// 449\n         var2[var5] = DigitOnes[var4];\n         --var5;// 450\n         var2[var5] = DigitTens[var4];\n      }\n\n      do {\n         var3 = var0 * '쳍' >>> 19;// 456\n         var4 = var0 - ((var3 << 3) + (var3 << 1));// 457\n         --var5;// 458\n         var2[var5] = digits[var4];\n         var0 = var3;// 459\n      } while(var3 != 0);// 460\n\n      if (var6 != 0) {// 462\n         --var5;\n         var2[var5] = (char)var6;// 463\n      }\n\n   }// 465\n\n   static int stringSize(int var0) {\n      int var1;\n      for(var1 = 0; var0 > sizeTable[var1]; ++var1) {// 472 473\n      }\n\n      return var1 + 1;// 474\n   }\n\n   public static int parseInt(String var0, int var1) throws NumberFormatException {\n      if (var0 == null) {// 541\n         throw new NumberFormatException(\"null\");// 542\n      } else if (var1 < 2) {// 545\n         throw new NumberFormatException(\"radix \" + var1 + \" less than Character.MIN_RADIX\");// 546\n      } else if (var1 > 36) {// 550\n         throw new NumberFormatException(\"radix \" + var1 + \" greater than Character.MAX_RADIX\");// 551\n      } else {\n         int var2 = 0;// 555\n         boolean var3 = false;// 556\n         int var4 = 0;// 557\n         int var5 = var0.length();\n         int var6 = -2147483647;// 558\n         if (var5 > 0) {// 562\n            char var9 = var0.charAt(0);// 563\n            if (var9 < '0') {// 564\n               if (var9 == '-') {// 565\n                  var3 = true;// 566\n                  var6 = MIN_VALUE;// 567\n               } else if (var9 != '+') {// 568\n                  throw NumberFormatException.forInputString(var0);// 569\n               }\n\n               if (var5 == 1) {// 571\n                  throw NumberFormatException.forInputString(var0);// 572\n               }\n\n               ++var4;// 573\n            }\n\n            int var8;\n            for(int var7 = var6 / var1; var4 < var5; var2 -= var8) {// 575 576 589\n               var8 = Character.digit(var0.charAt(var4++), var1);// 578\n               if (var8 < 0) {// 579\n                  throw NumberFormatException.forInputString(var0);// 580\n               }\n\n               if (var2 < var7) {// 582\n                  throw NumberFormatException.forInputString(var0);// 583\n               }\n\n               var2 *= var1;// 585\n               if (var2 < var6 + var8) {// 586\n                  throw NumberFormatException.forInputString(var0);// 587\n               }\n            }\n\n            return var3 ? var2 : -var2;// 594\n         } else {\n            throw NumberFormatException.forInputString(var0);// 592\n         }\n      }\n   }\n\n   public static int parseInt(String var0) throws NumberFormatException {\n      return parseInt(var0, 10);// 615\n   }\n\n   public static int parseUnsignedInt(String var0, int var1) throws NumberFormatException {\n      if (var0 == null) {// 663\n         throw new NumberFormatException(\"null\");// 664\n      } else {\n         int var2 = var0.length();// 667\n         if (var2 > 0) {// 668\n            char var3 = var0.charAt(0);// 669\n            if (var3 == '-') {// 670\n               throw new NumberFormatException(String.format(\"Illegal leading minus sign on unsigned string %s.\", var0));// 671 672\n            } else if (var2 <= 5 || var1 == 10 && var2 <= 9) {// 675\n               return parseInt(var0, var1);// 677\n            } else {\n               long var4 = Long.parseLong(var0, var1);// 679\n               if ((var4 & -4294967296L) == 0L) {// 680\n                  return (int)var4;// 681\n               } else {\n                  throw new NumberFormatException(String.format(\"String value %s exceeds range of unsigned int.\", var0));// 683 684\n               }\n            }\n         } else {\n            throw NumberFormatException.forInputString(var0);// 690\n         }\n      }\n   }\n\n   public static int parseUnsignedInt(String var0) throws NumberFormatException {\n      return parseUnsignedInt(var0, 10);// 711\n   }\n\n   public static Integer valueOf(String var0, int var1) throws NumberFormatException {\n      return parseInt(var0, var1);// 740\n   }\n\n   public static Integer valueOf(String var0) throws NumberFormatException {\n      return parseInt(var0, 10);// 766\n   }\n\n   public static Integer valueOf(int var0) {\n      return var0 >= -128 && var0 <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[var0 + 128] : new Integer(var0);// 830 831\n   }\n\n   public Integer(int var1) {\n      this.value = var1;// 850\n   }// 851\n\n   public Integer(String var1) throws NumberFormatException {\n      this.value = parseInt(var1, 10);// 867\n   }// 868\n\n   public byte byteValue() {\n      return (byte)this.value;// 876\n   }\n\n   public short shortValue() {\n      return (short)this.value;// 885\n   }\n\n   public int intValue() {\n      return this.value;// 893\n   }\n\n   public long longValue() {\n      return (long)this.value;// 903\n   }\n\n   public float floatValue() {\n      return (float)this.value;// 912\n   }\n\n   public double doubleValue() {\n      return (double)this.value;// 921\n   }\n\n   public String toString() {\n      return toString(this.value);// 935\n   }\n\n   public int hashCode() {\n      return hashCode(this.value);// 947\n   }\n\n   public static int hashCode(int var0) {\n      return var0;// 960\n   }\n\n   public boolean equals(Object var1) {\n      if (var1 instanceof Integer) {// 974\n         return this.value == (Integer)var1;// 975\n      } else {\n         return false;// 977\n      }\n   }\n\n   public static Integer getInteger(String var0) {\n      return getInteger(var0, (Integer)null);// 1011\n   }\n\n   public static Integer getInteger(String var0, int var1) {\n      Integer var2 = getInteger(var0, (Integer)null);// 1057\n      return var2 == null ? var1 : var2;// 1058\n   }\n\n   public static Integer getInteger(String var0, Integer var1) {\n      String var2 = null;// 1099\n\n      try {\n         var2 = System.getProperty(var0);// 1101\n      } catch (NullPointerException | IllegalArgumentException var4) {// 1102\n      }\n\n      if (var2 != null) {// 1104\n         try {\n            return decode(var2);// 1106\n         } catch (NumberFormatException var5) {// 1107\n         }\n      }\n\n      return var1;// 1110\n   }\n\n   public static Integer decode(String var0) throws NumberFormatException {\n      byte var1 = 10;// 1156\n      int var2 = 0;// 1157\n      boolean var3 = false;// 1158\n      if (var0.length() == 0) {// 1161\n         throw new NumberFormatException(\"Zero length string\");// 1162\n      } else {\n         char var5 = var0.charAt(0);// 1163\n         if (var5 == '-') {// 1165\n            var3 = true;// 1166\n            ++var2;// 1167\n         } else if (var5 == '+') {// 1168\n            ++var2;// 1169\n         }\n\n         if (!var0.startsWith(\"0x\", var2) && !var0.startsWith(\"0X\", var2)) {// 1172\n            if (var0.startsWith(\"#\", var2)) {// 1176\n               ++var2;// 1177\n               var1 = 16;// 1178\n            } else if (var0.startsWith(\"0\", var2) && var0.length() > 1 + var2) {// 1180\n               ++var2;// 1181\n               var1 = 8;// 1182\n            }\n         } else {\n            var2 += 2;// 1173\n            var1 = 16;// 1174\n         }\n\n         if (!var0.startsWith(\"-\", var2) && !var0.startsWith(\"+\", var2)) {// 1185\n            Integer var4;\n            try {\n               var4 = valueOf(var0.substring(var2), var1);// 1189\n               var4 = var3 ? -var4 : var4;// 1190\n            } catch (NumberFormatException var8) {\n               String var7 = var3 ? \"-\" + var0.substring(var2) : var0.substring(var2);// 1195 1196\n               var4 = valueOf(var7, var1);// 1197\n            }\n\n            return var4;// 1199\n         } else {\n            throw new NumberFormatException(\"Sign character in wrong position\");// 1186\n         }\n      }\n   }\n\n   public int compareTo(Integer var1) {\n      return compare(this.value, var1.value);// 1216\n   }\n\n   public static int compare(int var0, int var1) {\n      return var0 < var1 ? -1 : (var0 == var1 ? 0 : 1);// 1234\n   }\n\n   public static int compareUnsigned(int var0, int var1) {\n      return compare(var0 + MIN_VALUE, var1 + MIN_VALUE);// 1250\n   }\n\n   public static long toUnsignedLong(int var0) {\n      return (long)var0 & 4294967295L;// 1271\n   }\n\n   public static int divideUnsigned(int var0, int var1) {\n      return (int)(toUnsignedLong(var0) / toUnsignedLong(var1));// 1294\n   }\n\n   public static int remainderUnsigned(int var0, int var1) {\n      return (int)(toUnsignedLong(var0) % toUnsignedLong(var1));// 1311\n   }\n\n   public static int highestOneBit(int var0) {\n      var0 |= var0 >> 1;// 1348\n      var0 |= var0 >> 2;// 1349\n      var0 |= var0 >> 4;// 1350\n      var0 |= var0 >> 8;// 1351\n      var0 |= var0 >> 16;// 1352\n      return var0 - (var0 >>> 1);// 1353\n   }\n\n   public static int lowestOneBit(int var0) {\n      return var0 & -var0;// 1371\n   }\n\n   public static int numberOfLeadingZeros(int var0) {\n      if (var0 == 0) {// 1397\n         return 32;// 1398\n      } else {\n         int var1 = 1;// 1399\n         if (var0 >>> 16 == 0) {// 1400\n            var1 += 16;\n            var0 <<= 16;\n         }\n\n         if (var0 >>> 24 == 0) {// 1401\n            var1 += 8;\n            var0 <<= 8;\n         }\n\n         if (var0 >>> 28 == 0) {// 1402\n            var1 += 4;\n            var0 <<= 4;\n         }\n\n         if (var0 >>> 30 == 0) {\n            var1 += 2;\n            var0 <<= 2;// 1403\n         }\n\n         var1 -= var0 >>> 31;// 1404\n         return var1;// 1405\n      }\n   }\n\n   public static int numberOfTrailingZeros(int var0) {\n      if (var0 == 0) {// 1425\n         return 32;\n      } else {\n         int var2 = 31;// 1426\n         int var1 = var0 << 16;// 1427\n         if (var1 != 0) {\n            var2 -= 16;\n            var0 = var1;\n         }\n\n         var1 = var0 << 8;// 1428\n         if (var1 != 0) {\n            var2 -= 8;\n            var0 = var1;\n         }\n\n         var1 = var0 << 4;// 1429\n         if (var1 != 0) {\n            var2 -= 4;\n            var0 = var1;\n         }\n\n         var1 = var0 << 2;\n         if (var1 != 0) {\n            var2 -= 2;// 1430\n            var0 = var1;\n         }\n\n         return var2 - (var0 << 1 >>> 31);// 1431\n      }\n   }\n\n   public static int bitCount(int var0) {\n      var0 -= var0 >>> 1 & 1431655765;// 1446\n      var0 = (var0 & 858993459) + (var0 >>> 2 & 858993459);// 1447\n      var0 = var0 + (var0 >>> 4) & 252645135;// 1448\n      var0 += var0 >>> 8;// 1449\n      var0 += var0 >>> 16;// 1450\n      return var0 & 63;// 1451\n   }\n\n   public static int rotateLeft(int var0, int var1) {\n      return var0 << var1 | var0 >>> -var1;// 1475\n   }\n\n   public static int rotateRight(int var0, int var1) {\n      return var0 >>> var1 | var0 << -var1;// 1499\n   }\n\n   public static int reverse(int var0) {\n      var0 = (var0 & 1431655765) << 1 | var0 >>> 1 & 1431655765;// 1514\n      var0 = (var0 & 858993459) << 2 | var0 >>> 2 & 858993459;// 1515\n      var0 = (var0 & 252645135) << 4 | var0 >>> 4 & 252645135;// 1516\n      var0 = var0 << 24 | (var0 & '\\uff00') << 8 | var0 >>> 8 & '\\uff00' | var0 >>> 24;// 1517\n      return var0;// 1519\n   }\n\n   public static int signum(int var0) {\n      return var0 >> 31 | -var0 >>> 31;// 1533\n   }\n\n   public static int reverseBytes(int var0) {\n      return var0 >>> 24 | var0 >> 8 & '\\uff00' | var0 << 8 & 16711680 | var0 << 24;// 1546\n   }\n\n   public static int sum(int var0, int var1) {\n      return var0 + var1;// 1562\n   }\n\n   public static int max(int var0, int var1) {\n      return Math.max(var0, var1);// 1576\n   }\n\n   public static int min(int var0, int var1) {\n      return Math.min(var0, var1);// 1590\n   }\n\n   private static class IntegerCache {\n      static final int low = -128;\n      static final int high;\n      static final Integer[] cache;\n\n      static {\n         int var0 = 127;// 787\n         String var1 = VM.getSavedProperty(\"java.lang.Integer.IntegerCache.high\");// 788 789\n         int var2;\n         if (var1 != null) {// 790\n            try {\n               var2 = Integer.parseInt(var1);// 792\n               var2 = Math.max(var2, 127);// 793\n               var0 = Math.min(var2, 2147483518);// 795\n            } catch (NumberFormatException var4) {// 796\n            }\n         }\n\n         high = var0;// 800\n         cache = new Integer[high - -128 + 1];// 802\n         var2 = -128;// 803\n\n         for(int var3 = 0; var3 < cache.length; ++var3) {// 804\n            cache[var3] = new Integer(var2++);// 805\n         }\n\n         assert high >= 127;// 808\n\n      }// 809\n   }\n}\n\nclass 'java/lang/Integer' {\n   method 'toString (II)Ljava/lang/String;' {\n      1      18\n      2      18\n      6      18\n      8      18\n      b      19\n      d      19\n      f      22\n      11      22\n      15      23\n      18      23\n      19      25\n      1d      25\n      1f      26\n      27      26\n      28      27\n      2a      27\n      2d      28\n      31      29\n      32      29\n      35      32\n      36      32\n      3c      33\n      3f      33\n      44      33\n      45      33\n      46      33\n      47      33\n      4b      34\n      52      37\n      56      37\n      57      37\n      58      37\n      5a      38\n      5e      39\n      63      40\n      65      40\n      6d      43\n      71      43\n      75      43\n   }\n\n   method 'toUnsignedString (II)Ljava/lang/String;' {\n      1      48\n      5      48\n      8      48\n   }\n\n   method 'toHexString (I)Ljava/lang/String;' {\n      1      52\n      2      52\n      5      52\n   }\n\n   method 'toOctalString (I)Ljava/lang/String;' {\n      1      56\n      2      56\n      5      56\n   }\n\n   method 'toBinaryString (I)Ljava/lang/String;' {\n      1      60\n      2      60\n      5      60\n   }\n\n   method 'toUnsignedString0 (II)Ljava/lang/String;' {\n      0      64\n      3      64\n      6      64\n      7      64\n      a      65\n      b      65\n      c      65\n      e      65\n      f      65\n      10      65\n      13      65\n      17      66\n      1d      67\n      1f      67\n      29      68\n      2d      68\n   }\n\n   method 'formatUnsignedInt (II[CII)I' {\n      2      72\n      4      73\n      6      73\n      7      73\n      b      74\n      c      74\n      d      74\n      11      77\n      16      78\n      17      78\n      1d      78\n      1e      78\n      1f      78\n      23      79\n      25      80\n      2a      80\n      2f      82\n   }\n\n   method 'toString (I)Ljava/lang/String;' {\n      1      86\n      3      86\n      6      87\n      8      87\n      a      89\n      e      89\n      f      89\n      12      89\n      13      89\n      18      89\n      1b      89\n      1f      90\n      23      91\n      2b      92\n      2f      92\n   }\n\n   method 'toUnsignedString (I)Ljava/lang/String;' {\n      1      97\n      4      97\n      7      97\n   }\n\n   method 'getChars (II[C)V' {\n      1      101\n      3      102\n      4      102\n      7      103\n      a      104\n      c      104\n      f      105\n      10      105\n      12      110\n      14      110\n      18      111\n      1a      111\n      1b      111\n      1e      112\n      20      112\n      22      112\n      23      112\n      24      112\n      26      112\n      27      112\n      28      112\n      29      112\n      2a      112\n      2d      113\n      2f      114\n      34      115\n      39      115\n      3a      115\n      3c      116\n      41      117\n      46      117\n      47      117\n      4c      121\n      4e      121\n      4f      121\n      51      121\n      52      121\n      55      122\n      56      122\n      58      122\n      59      122\n      5a      122\n      5b      122\n      5c      122\n      5f      123\n      64      124\n      69      124\n      6a      124\n      6c      125\n      6e      126\n      76      128\n      7a      129\n      81      130\n      82      133\n   }\n\n   method 'stringSize (I)I' {\n      0      137\n      1      137\n      3      137\n      7      137\n      8      137\n      c      140\n      d      140\n      e      140\n      f      137\n   }\n\n   method 'parseInt (Ljava/lang/String;I)I' {\n      1      144\n      8      145\n      d      145\n      f      146\n      10      146\n      1e      147\n      27      147\n      2c      147\n      32      147\n      34      148\n      36      148\n      44      149\n      4d      149\n      52      149\n      58      149\n      59      151\n      5a      151\n      5b      152\n      5c      152\n      5d      153\n      5e      153\n      61      154\n      64      154\n      66      155\n      68      155\n      6c      156\n      70      157\n      71      157\n      74      157\n      78      158\n      7a      158\n      7f      159\n      81      159\n      84      160\n      85      160\n      86      161\n      88      161\n      8f      162\n      91      162\n      95      163\n      98      163\n      9b      166\n      9c      166\n      a0      167\n      a3      167\n      a4      170\n      aa      174\n      ab      174\n      b1      174\n      b7      175\n      ba      175\n      be      175\n      c1      175\n      c5      176\n      c9      177\n      cc      177\n      d0      180\n      d4      181\n      d7      181\n      db      184\n      e1      185\n      e2      185\n      e6      186\n      e9      186\n      ee      174\n      f6      192\n      f9      192\n      fb      190\n      103      190\n      104      190\n   }\n\n   method 'parseInt (Ljava/lang/String;)I' {\n      1      198\n      3      198\n      6      198\n   }\n\n   method 'parseUnsignedInt (Ljava/lang/String;I)I' {\n      1      202\n      8      203\n      d      203\n      f      205\n      12      205\n      14      206\n      18      207\n      19      207\n      1c      207\n      1e      208\n      20      208\n      27      209\n      31      209\n      37      209\n      39      210\n      3a      210\n      3e      210\n      40      210\n      44      210\n      46      210\n      4b      211\n      4e      211\n      51      213\n      54      213\n      58      214\n      5b      214\n      5c      214\n      5d      214\n      5e      214\n      63      215\n      64      215\n      69      217\n      73      217\n      79      217\n      7b      221\n      7e      221\n   }\n\n   method 'parseUnsignedInt (Ljava/lang/String;)I' {\n      1      227\n      3      227\n      6      227\n   }\n\n   method 'valueOf (Ljava/lang/String;I)Ljava/lang/Integer;' {\n      2      231\n      5      231\n      8      231\n   }\n\n   method 'valueOf (Ljava/lang/String;)Ljava/lang/Integer;' {\n      1      235\n      3      235\n      6      235\n      9      235\n   }\n\n   method 'valueOf (I)Ljava/lang/Integer;' {\n      1      239\n      3      239\n      7      239\n      a      239\n      d      239\n      11      239\n      14      239\n      15      239\n   }\n\n   method '<init> (I)V' {\n      6      243\n      9      244\n   }\n\n   method '<init> (Ljava/lang/String;)V' {\n      6      247\n      8      247\n      b      247\n      e      248\n   }\n\n   method 'byteValue ()B' {\n      1      251\n      4      251\n      5      251\n   }\n\n   method 'shortValue ()S' {\n      1      255\n      4      255\n      5      255\n   }\n\n   method 'intValue ()I' {\n      1      259\n      4      259\n   }\n\n   method 'longValue ()J' {\n      1      263\n      4      263\n      5      263\n   }\n\n   method 'floatValue ()F' {\n      1      267\n      4      267\n      5      267\n   }\n\n   method 'doubleValue ()D' {\n      1      271\n      4      271\n      5      271\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      1      275\n      4      275\n      7      275\n   }\n\n   method 'hashCode ()I' {\n      1      279\n      4      279\n      7      279\n   }\n\n   method 'hashCode (I)I' {\n      1      283\n   }\n\n   method 'equals (Ljava/lang/Object;)Z' {\n      1      287\n      4      287\n      8      288\n      c      288\n      f      288\n      12      288\n      1a      288\n      1b      290\n      1c      290\n   }\n\n   method 'getInteger (Ljava/lang/String;)Ljava/lang/Integer;' {\n      1      295\n      2      295\n      5      295\n   }\n\n   method 'getInteger (Ljava/lang/String;I)Ljava/lang/Integer;' {\n      1      299\n      2      299\n      5      299\n      7      300\n      b      300\n      12      300\n   }\n\n   method 'getInteger (Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/Integer;' {\n      0      304\n      1      304\n      3      307\n      6      307\n      a      308\n      c      311\n      10      313\n      13      313\n      14      314\n      16      318\n   }\n\n   method 'decode (Ljava/lang/String;)Ljava/lang/Integer;' {\n      0      322\n      2      322\n      3      323\n      4      323\n      5      324\n      6      324\n      8      325\n      b      325\n      12      326\n      17      326\n      19      328\n      1a      328\n      1d      328\n      21      329\n      23      329\n      26      330\n      27      330\n      28      331\n      30      332\n      32      332\n      35      333\n      39      336\n      3c      336\n      3f      336\n      43      336\n      46      336\n      49      336\n      4c      345\n      4f      346\n      51      346\n      56      337\n      59      337\n      5c      337\n      5f      338\n      62      339\n      64      339\n      69      340\n      6c      340\n      6f      340\n      73      340\n      76      340\n      78      340\n      79      340\n      7c      341\n      7f      342\n      81      342\n      83      349\n      86      349\n      89      349\n      8d      349\n      90      349\n      93      349\n      9a      361\n      9f      361\n      a2      352\n      a6      352\n      a9      352\n      ac      353\n      b1      353\n      b4      353\n      b5      353\n      bd      353\n      c5      355\n      cf      355\n      d6      355\n      dc      355\n      e4      355\n      e7      354\n      ec      356\n      ef      356\n      f3      359\n   }\n\n   method 'compareTo (Ljava/lang/Integer;)I' {\n      1      367\n      5      367\n      8      367\n      b      367\n   }\n\n   method 'compare (II)I' {\n      2      371\n      5      371\n      b      371\n      e      371\n      12      371\n      13      371\n   }\n\n   method 'compareUnsigned (II)I' {\n      1      375\n      3      375\n      5      375\n      7      375\n      8      375\n      b      375\n   }\n\n   method 'toUnsignedLong (I)J' {\n      1      379\n      2      379\n      5      379\n      6      379\n   }\n\n   method 'divideUnsigned (II)I' {\n      1      383\n      5      383\n      8      383\n      9      383\n      a      383\n   }\n\n   method 'remainderUnsigned (II)I' {\n      1      387\n      5      387\n      8      387\n      9      387\n      a      387\n   }\n\n   method 'highestOneBit (I)I' {\n      2      391\n      3      391\n      5      391\n      8      392\n      9      392\n      b      392\n      e      393\n      f      393\n      11      393\n      14      394\n      16      394\n      18      394\n      1b      395\n      1d      395\n      1f      395\n      22      396\n      23      396\n      24      396\n      25      396\n   }\n\n   method 'lowestOneBit (I)I' {\n      2      400\n      3      400\n      4      400\n   }\n\n   method 'numberOfLeadingZeros (I)I' {\n      1      404\n      4      405\n      6      405\n      7      407\n      8      407\n      a      408\n      c      408\n      d      408\n      10      409\n      14      410\n      17      410\n      19      413\n      1b      413\n      1c      413\n      1f      414\n      23      415\n      26      415\n      28      418\n      2a      418\n      2b      418\n      2e      419\n      32      420\n      34      420\n      36      423\n      38      423\n      39      423\n      3c      424\n      40      425\n      42      425\n      45      428\n      47      428\n      49      428\n      4b      429\n   }\n\n   method 'numberOfTrailingZeros (I)I' {\n      1      434\n      4      435\n      6      435\n      7      437\n      9      437\n      b      438\n      d      438\n      e      438\n      10      439\n      14      440\n      17      440\n      19      441\n      1b      444\n      1d      444\n      1e      444\n      20      445\n      24      446\n      27      446\n      29      447\n      2b      450\n      2c      450\n      2d      450\n      2f      451\n      33      452\n      35      452\n      37      453\n      39      456\n      3a      456\n      3b      456\n      3d      457\n      41      458\n      43      458\n      45      459\n      48      462\n      49      462\n      4a      462\n      4c      462\n      4d      462\n      4e      462\n   }\n\n   method 'bitCount (I)I' {\n      2      467\n      3      467\n      4      467\n      6      467\n      8      467\n      a      468\n      c      468\n      e      468\n      f      468\n      10      468\n      12      468\n      13      468\n      14      468\n      17      469\n      18      469\n      19      469\n      1a      469\n      1c      469\n      1d      469\n      20      470\n      22      470\n      24      470\n      27      471\n      29      471\n      2b      471\n      2d      472\n      2f      472\n      30      472\n   }\n\n   method 'rotateLeft (II)I' {\n      2      476\n      5      476\n      6      476\n      7      476\n      8      476\n   }\n\n   method 'rotateRight (II)I' {\n      2      480\n      5      480\n      6      480\n      7      480\n      8      480\n   }\n\n   method 'reverse (I)I' {\n      1      484\n      3      484\n      4      484\n      5      484\n      7      484\n      8      484\n      9      484\n      b      484\n      c      484\n      d      484\n      f      485\n      11      485\n      12      485\n      13      485\n      15      485\n      16      485\n      17      485\n      19      485\n      1a      485\n      1b      485\n      1d      486\n      1f      486\n      20      486\n      21      486\n      23      486\n      24      486\n      25      486\n      27      486\n      28      486\n      29      486\n      2b      487\n      2d      487\n      2f      487\n      31      487\n      32      487\n      34      487\n      35      487\n      37      487\n      39      487\n      3a      487\n      3c      487\n      3d      487\n      3f      487\n      41      487\n      42      487\n      43      487\n      45      488\n   }\n\n   method 'signum (I)I' {\n      1      492\n      3      492\n      5      492\n      6      492\n      8      492\n      9      492\n      a      492\n   }\n\n   method 'reverseBytes (I)I' {\n      1      496\n      3      496\n      5      496\n      7      496\n      8      496\n      a      496\n      b      496\n      d      496\n      f      496\n      10      496\n      12      496\n      13      496\n      15      496\n      17      496\n      18      496\n      19      496\n   }\n\n   method 'sum (II)I' {\n      2      500\n      3      500\n   }\n\n   method 'max (II)I' {\n      2      504\n      5      504\n   }\n\n   method 'min (II)I' {\n      2      508\n      5      508\n   }\n}\n\nclass 'java/lang/Integer$IntegerCache' {\n   method '<clinit> ()V' {\n      10      517\n      12      517\n      13      518\n      15      518\n      18      518\n      1a      520\n      1e      522\n      21      522\n      23      523\n      25      523\n      28      523\n      2a      524\n      2c      524\n      2f      524\n      33      525\n      35      529\n      38      530\n      3b      530\n      3d      530\n      3e      530\n      3f      530\n      43      530\n      46      531\n      48      531\n      49      533\n      4a      533\n      4c      533\n      4f      533\n      50      533\n      53      534\n      5c      534\n      62      534\n      63      533\n      6f      537\n      72      537\n      74      537\n      7f      539\n   }\n}\n\nLines mapping:\n131 <-> 19\n132 <-> 20\n135 <-> 23\n136 <-> 24\n139 <-> 26\n140 <-> 27\n141 <-> 28\n143 <-> 29\n144 <-> 30\n147 <-> 33\n148 <-> 34\n149 <-> 35\n151 <-> 38\n153 <-> 39\n154 <-> 40\n157 <-> 44\n187 <-> 49\n233 <-> 53\n271 <-> 57\n303 <-> 61\n311 <-> 65\n312 <-> 66\n313 <-> 67\n315 <-> 68\n318 <-> 69\n331 <-> 73\n332 <-> 74\n333 <-> 75\n335 <-> 78\n336 <-> 80\n337 <-> 81\n339 <-> 83\n398 <-> 87\n399 <-> 88\n400 <-> 90\n401 <-> 91\n402 <-> 92\n403 <-> 93\n421 <-> 98\n435 <-> 102\n436 <-> 103\n438 <-> 104\n439 <-> 105\n440 <-> 106\n444 <-> 111\n445 <-> 112\n447 <-> 113\n448 <-> 114\n449 <-> 115\n450 <-> 117\n456 <-> 122\n457 <-> 123\n458 <-> 124\n459 <-> 126\n460 <-> 127\n462 <-> 129\n463 <-> 131\n465 <-> 134\n472 <-> 138\n473 <-> 138\n474 <-> 141\n541 <-> 145\n542 <-> 146\n545 <-> 147\n546 <-> 148\n550 <-> 149\n551 <-> 150\n555 <-> 152\n556 <-> 153\n557 <-> 154\n558 <-> 156\n562 <-> 157\n563 <-> 158\n564 <-> 159\n565 <-> 160\n566 <-> 161\n567 <-> 162\n568 <-> 163\n569 <-> 164\n571 <-> 167\n572 <-> 168\n573 <-> 171\n575 <-> 175\n576 <-> 175\n578 <-> 176\n579 <-> 177\n580 <-> 178\n582 <-> 181\n583 <-> 182\n585 <-> 185\n586 <-> 186\n587 <-> 187\n589 <-> 175\n592 <-> 193\n594 <-> 191\n615 <-> 199\n663 <-> 203\n664 <-> 204\n667 <-> 206\n668 <-> 207\n669 <-> 208\n670 <-> 209\n671 <-> 210\n672 <-> 210\n675 <-> 211\n677 <-> 212\n679 <-> 214\n680 <-> 215\n681 <-> 216\n683 <-> 218\n684 <-> 218\n690 <-> 222\n711 <-> 228\n740 <-> 232\n766 <-> 236\n787 <-> 518\n788 <-> 519\n789 <-> 519\n790 <-> 521\n792 <-> 523\n793 <-> 524\n795 <-> 525\n796 <-> 526\n800 <-> 530\n802 <-> 531\n803 <-> 532\n804 <-> 534\n805 <-> 535\n808 <-> 538\n809 <-> 540\n830 <-> 240\n831 <-> 240\n850 <-> 244\n851 <-> 245\n867 <-> 248\n868 <-> 249\n876 <-> 252\n885 <-> 256\n893 <-> 260\n903 <-> 264\n912 <-> 268\n921 <-> 272\n935 <-> 276\n947 <-> 280\n960 <-> 284\n974 <-> 288\n975 <-> 289\n977 <-> 291\n1011 <-> 296\n1057 <-> 300\n1058 <-> 301\n1099 <-> 305\n1101 <-> 308\n1102 <-> 309\n1104 <-> 312\n1106 <-> 314\n1107 <-> 315\n1110 <-> 319\n1156 <-> 323\n1157 <-> 324\n1158 <-> 325\n1161 <-> 326\n1162 <-> 327\n1163 <-> 329\n1165 <-> 330\n1166 <-> 331\n1167 <-> 332\n1168 <-> 333\n1169 <-> 334\n1172 <-> 337\n1173 <-> 346\n1174 <-> 347\n1176 <-> 338\n1177 <-> 339\n1178 <-> 340\n1180 <-> 341\n1181 <-> 342\n1182 <-> 343\n1185 <-> 350\n1186 <-> 362\n1189 <-> 353\n1190 <-> 354\n1195 <-> 356\n1196 <-> 356\n1197 <-> 357\n1199 <-> 360\n1216 <-> 368\n1234 <-> 372\n1250 <-> 376\n1271 <-> 380\n1294 <-> 384\n1311 <-> 388\n1348 <-> 392\n1349 <-> 393\n1350 <-> 394\n1351 <-> 395\n1352 <-> 396\n1353 <-> 397\n1371 <-> 401\n1397 <-> 405\n1398 <-> 406\n1399 <-> 408\n1400 <-> 409\n1401 <-> 414\n1402 <-> 419\n1403 <-> 426\n1404 <-> 429\n1405 <-> 430\n1425 <-> 435\n1426 <-> 438\n1427 <-> 439\n1428 <-> 445\n1429 <-> 451\n1430 <-> 459\n1431 <-> 463\n1446 <-> 468\n1447 <-> 469\n1448 <-> 470\n1449 <-> 471\n1450 <-> 472\n1451 <-> 473\n1475 <-> 477\n1499 <-> 481\n1514 <-> 485\n1515 <-> 486\n1516 <-> 487\n1517 <-> 488\n1519 <-> 489\n1533 <-> 493\n1546 <-> 497\n1562 <-> 501\n1576 <-> 505\n1590 <-> 509\nNot mapped:\n591\n780\n798\n832\n849\n866\n1103\n1191\n1198\n"
  },
  {
    "path": "testData/results/InterfaceNonSealed.dec",
    "content": "package sealed;\n\nnon-sealed interface InterfaceNonSealed extends RootWithInterfaceOuter {\n}\n\n"
  },
  {
    "path": "testData/results/InterfaceSuperTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\npublic interface InterfaceSuperTypeAnnotations extends @B Bar, @L @B BarGeneric<@F String, @L @A String @B []> {\n}\n\n"
  },
  {
    "path": "testData/results/InvalidMethodSignature.dec",
    "content": "package a.a.a.a.e.f;\n\nimport a.a.a.a.a.k;\nimport a.a.a.a.c.c;\nimport a.a.a.a.c.j;\nimport a.a.a.a.e.bg;\nimport java.io.File;\n\nclass i implements bg {\n   private final j a;\n   private final b b;\n\n   i(b var1, j var2) {\n      this.b = var1;// 1\n      this.a = var2;\n   }\n\n   public void a(c var1, k var2, boolean var3) {\n      File var4 = this.a.b().a(var1);// 2\n      a.a.a.a.e.f.b.a(this.b).add(var4);// 3\n   }// 4\n\n   public void a(a.a.a.a.c.b var1) {\n   }// 0\n}\n\nclass 'a/a/a/a/e/f/i' {\n   method '<init> (La/a/a/a/e/f/b;La/a/a/a/c/j;)V' {\n      2      13\n      7      14\n      e      15\n   }\n\n   method 'a (La/a/a/a/c/c;La/a/a/a/a/k;Z)V' {\n      1      18\n      4      18\n      a      18\n      f      18\n      12      19\n      15      19\n      1a      19\n      20      20\n   }\n\n   method 'a (La/a/a/a/c/b;)V' {\n      0      23\n   }\n}\n\nLines mapping:\n0 <-> 24\n1 <-> 14\n2 <-> 19\n3 <-> 20\n4 <-> 21\n"
  },
  {
    "path": "testData/results/MemberDeclarationTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.function.Consumer;\n\npublic class MemberDeclarationTypeAnnotations<@A P extends @B Number & @F Serializable> {\n   @L String s1 = \"\";\n   @B int f1 = 0;\n   Consumer<String> c = (@A String s) -> {\n      System.out.println(s);// 12\n   };\n   SomeFunInterface<String, String> sf = (String s1, @B String s2) -> {\n      System.out.println(s1);// 14\n   };\n\n   @K\n   public @L @A MemberDeclarationTypeAnnotations() {\n   }// 18\n\n   @K\n   public <@A T extends @B Number & @F Serializable> @L @C Number foo(@D T @E [] a) {\n      return 0;// 22\n   }\n\n   @K\n   public <T> @C Number bar(@D T @E [] a) throws @A IOException, @B IllegalStateException {\n      return 0;// 27\n   }\n\n   public void fooBar(@L @A String param1, @K @L @B String param2) {\n   }// 30\n}\n\nclass 'typeAnnotations/MemberDeclarationTypeAnnotations' {\n   method 'lambda$new$0 (Ljava/lang/String;)V' {\n      0      10\n      4      10\n      7      11\n   }\n\n   method 'lambda$new$1 (Ljava/lang/String;Ljava/lang/String;)V' {\n      0      13\n      4      13\n      7      14\n   }\n\n   method '<init> ()V' {\n      21      18\n   }\n\n   method 'foo ([Ljava/lang/Number;)Ljava/lang/Number;' {\n      0      22\n      1      22\n      4      22\n   }\n\n   method 'bar ([Ljava/lang/Object;)Ljava/lang/Number;' {\n      0      27\n      1      27\n      4      27\n   }\n\n   method 'fooBar (Ljava/lang/String;Ljava/lang/String;)V' {\n      0      31\n   }\n}\n\nLines mapping:\n12 <-> 11\n14 <-> 14\n18 <-> 19\n22 <-> 23\n27 <-> 28\n30 <-> 32\nNot mapped:\n8\n10\n17\n"
  },
  {
    "path": "testData/results/MoreAnnotations.dec",
    "content": "package pkg;\n\npublic @interface MoreAnnotations {\n   @MoreAnnotations(\n      intValue = 1,\n      byteValue = 1,\n      floatValue = 1.0F,\n      doubleValue = 1.0,\n      booleanValue = true,\n      shortValue = 1,\n      longValue = 1L,\n      charValue = '\\n',\n      enumValue = MoreAnnotations.TestEnum.FirstValue,\n      annotationValue = @MoreAnnotations.NestedAnnotation(\"a\"),\n      stringValue = \"\",\n      classValue = String.class\n   )\n   String annotatedWithValues = \"\";\n   @MoreAnnotations(\n      intArray = {},\n      byteArray = {},\n      floatArray = {},\n      doubleArray = {},\n      booleanArray = {},\n      shortArray = {},\n      longArray = {},\n      charArray = {},\n      enumArray = {},\n      annotationArray = {},\n      stringArray = {},\n      classArray = {}\n   )\n   String annotatedWithEmptyArrays = \"\";\n   @MoreAnnotations(\n      intArray = {1, 0, 2147483647, -2147483648},\n      byteArray = {1, 0, 127, -128, -1},\n      floatArray = {1.0F, 0.0F, 3.4028235E38F, 1.4E-45F, 0.0F / 0.0F, 1.0F / 0.0F, -1.0F / 0.0F},\n      doubleArray = {1.0, 0.0, 1.7976931348623157E308, 4.9E-324, 0.0 / 0.0, 1.0 / 0.0, -1.0 / 0.0},\n      booleanArray = {true, false},\n      shortArray = {1, 0, 32767, -32768, -1},\n      longArray = {1L, 0L, 9223372036854775807L, -9223372036854775808L},\n      charArray = {'a', '\\n', '\\u0001', '\\u0000', '\\uffff', '\\u0000'},\n      enumArray = {MoreAnnotations.TestEnum.FirstValue, MoreAnnotations.TestEnum.SecondValue},\n      annotationArray = {@MoreAnnotations.NestedAnnotation(\"a\"), @MoreAnnotations.NestedAnnotation(\"b\")},\n      stringArray = {\"first\", \"second\", \"\"},\n      classArray = {CharSequence.class, String.class, StringBuilder.class}\n   )\n   String annotatedWithArrays = \"\";\n\n   int intValue() default 1;\n\n   byte byteValue() default 1;\n\n   float floatValue() default 1.0F / 0.0F;\n\n   double doubleValue() default 0.0 / 0.0;\n\n   boolean booleanValue() default true;\n\n   short shortValue() default 1;\n\n   long longValue() default 1L;\n\n   char charValue() default '0';\n\n   TestEnum enumValue() default MoreAnnotations.TestEnum.FirstValue;\n\n   NestedAnnotation annotationValue() default @MoreAnnotations.NestedAnnotation;\n\n   String stringValue() default \"default\";\n\n   Class<? extends CharSequence> classValue() default CharSequence.class;\n\n   int[] intArray() default {1, 0, 2147483647, -2147483648};\n\n   byte[] byteArray() default {1, 0, 127, -128, -1};\n\n   float[] floatArray() default {1.0F, 0.0F, 3.4028235E38F, 1.4E-45F, 0.0F / 0.0F, 1.0F / 0.0F, -1.0F / 0.0F};\n\n   double[] doubleArray() default {1.0, 0.0, 1.7976931348623157E308, 4.9E-324, 0.0 / 0.0, 1.0 / 0.0, -1.0 / 0.0};\n\n   boolean[] booleanArray() default {true, false};\n\n   short[] shortArray() default {1, 0, 32767, -32768, -1};\n\n   long[] longArray() default {1L, 0L, 9223372036854775807L, -9223372036854775808L};\n\n   char[] charArray() default {'\\u0001', '\\u0000', '\\uffff', '\\u0000'};\n\n   TestEnum[] enumArray() default {MoreAnnotations.TestEnum.FirstValue};\n\n   NestedAnnotation[] annotationArray() default {@MoreAnnotations.NestedAnnotation};\n\n   String[] stringArray() default {\"first\", \"second\", \"\"};\n\n   Class<? extends CharSequence>[] classArray() default {CharSequence.class, String.class, StringBuilder.class};\n\n   public static enum TestEnum {\n      FirstValue,\n      SecondValue;\n   }\n\n   public @interface NestedAnnotation {\n      String value() default \"MyString\";\n   }\n}\n\n"
  },
  {
    "path": "testData/results/NestedType.dec",
    "content": "package pkg;\n\npublic class NestedType {\n   public void doSomething() {\n      Foo foo = new Foo();// 5\n      Foo.Bar bar = new Foo.Bar();// 6\n      System.out.println(foo);// 7\n      System.out.println(bar);// 8\n      new FooBar();\n      System.out.println(foo);// 11\n   }// 12\n\n   static class Foo {\n      public void doSomething() {\n         Bar fooBar = new Bar();// 16\n         FooBar.Bar<String, String> fooBarBar = new FooBar.Bar();// 17\n         System.out.println(fooBar);// 18\n         System.out.println(fooBarBar);// 19\n      }// 20\n\n      static class Bar {\n      }\n   }\n\n   static class FooBar<T> {\n      public void doSomething() {\n         Foo.Bar fooBar = new Foo.Bar();// 27\n         Bar<String, String> fooBarBar = new Bar();// 28\n         System.out.println(fooBar);// 29\n         System.out.println(fooBarBar);// 30\n      }// 31\n\n      static class Bar<T, E> {\n      }\n   }\n}\n\nclass 'pkg/NestedType' {\n   method 'doSomething ()V' {\n      7      4\n      f      5\n      10      6\n      14      6\n      17      7\n      1b      7\n      26      9\n      2a      9\n      2d      10\n   }\n}\n\nclass 'pkg/NestedType$Foo' {\n   method 'doSomething ()V' {\n      7      14\n      f      15\n      10      16\n      14      16\n      17      17\n      1b      17\n      1e      18\n   }\n}\n\nclass 'pkg/NestedType$FooBar' {\n   method 'doSomething ()V' {\n      7      26\n      f      27\n      10      28\n      14      28\n      17      29\n      1b      29\n      1e      30\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 6\n7 <-> 7\n8 <-> 8\n11 <-> 10\n12 <-> 11\n16 <-> 15\n17 <-> 16\n18 <-> 17\n19 <-> 18\n20 <-> 19\n27 <-> 27\n28 <-> 28\n29 <-> 29\n30 <-> 30\n31 <-> 31\nNot mapped:\n10\n"
  },
  {
    "path": "testData/results/NestedTypeAnnotations.dec",
    "content": "package typeAnnotations;\n\npublic class NestedTypeAnnotations {\n   @A Z.Y.X.W w1;\n   Z.@B Y.X.W w2;\n   Z.Y.@C X.W w3;\n   Z.Y.X.@D W w4;\n   @A Z.@B Y.@C X.@D W w5;\n   @L Z.Y.X.W w6;\n   P.@A Q.R r1;\n   P.Q.@B R r2;\n   S.T.@C U u1;\n   T.@A Y.U.I.O o1;\n}\n\n"
  },
  {
    "path": "testData/results/PrivateClasses.dec",
    "content": "package pkg;\n\nclass PrivateClasses {\n   private static final Runnable R1 = new Runnable() {\n      public void run() {\n         final String s = \"\";// 11\n\n         class NonCapturingLocalR1 {\n            private final String s;\n\n            public NonCapturingLocalR1(String s) {\n               this.s = s;// 17\n            }// 18\n\n            public String toString() {\n               return this.s;// 22\n            }\n         }\n\n         (new NonCapturingLocalR1(s)).toString();// 39\n\n         class CapturingLocalR1 {\n            private final int i;\n\n            public CapturingLocalR1(int i) {\n               this.i = i;// 30\n            }// 31\n\n            public String toString() {\n               return s + \":\" + this.i;// 35\n            }\n         }\n\n         (new CapturingLocalR1(42)).toString();// 40\n         Callable<String> c1 = new Callable<String>() {\n            public String call() {\n               return null;// 45\n            }\n         };// 42\n         Callable<String> c2 = new Callable<String>() {\n            public String call() {\n               return s;// 52\n            }\n         };// 49\n         ((String)c1.call() + (String)c2.call()).length();// 56\n      }// 57\n   };\n   private final Runnable R2 = new Runnable() {\n      public void run() {\n         final String s = \"\";// 63\n\n         class NonCapturingLocalR2 {\n            private final String s;\n\n            public NonCapturingLocalR2(String s) {\n               this.s = s;// 69\n            }// 70\n\n            public String toString() {\n               return this.s;// 74\n            }\n         }\n\n         (new NonCapturingLocalR2(s)).toString();// 91\n\n         class CapturingLocalR1 {\n            private final int i;\n\n            public CapturingLocalR1(int i) {\n               this.i = i;// 82\n            }// 83\n\n            public String toString() {\n               return s + \":\" + this.i;// 87\n            }\n         }\n\n         (new CapturingLocalR1(42)).toString();// 92\n         Callable<String> c1 = new Callable<String>() {\n            public String call() {\n               return null;// 97\n            }\n         };// 94\n         Callable<String> c2 = new Callable<String>() {\n            public String call() {\n               return s;// 104\n            }\n         };// 101\n         ((String)c1.call() + (String)c2.call()).length();// 108\n      }// 109\n   };\n\n   public static void m1(final String s) {\n      class NonCapturingLocalM1 {\n         private final String s;\n\n         public NonCapturingLocalM1(String s) {\n            this.s = s;// 117\n         }// 118\n\n         public String toString() {\n            return this.s;// 122\n         }\n      }\n\n      (new NonCapturingLocalM1(s)).toString();// 139\n\n      class CapturingLocalM1 {\n         private final int i;\n\n         public CapturingLocalM1(int i) {\n            this.i = i;// 130\n         }// 131\n\n         public String toString() {\n            return s + \":\" + this.i;// 135\n         }\n      }\n\n      (new CapturingLocalM1(42)).toString();// 140\n      Callable<String> c1 = new Callable<String>() {\n         public String call() {\n            return null;// 145\n         }\n      };// 142\n      Callable<String> c2 = new Callable<String>() {\n         public String call() {\n            return s;// 152\n         }\n      };// 149\n      ((String)c1.call() + (String)c2.call()).length();// 156\n   }// 157\n\n   public void m2(final String s) {\n      class NonCapturingLocalM2 {\n         private final String s;\n\n         public NonCapturingLocalM2(String s) {\n            this.s = s;// 164\n         }// 165\n\n         public String toString() {\n            return this.s;// 169\n         }\n      }\n\n      (new NonCapturingLocalM2(s)).toString();// 186\n\n      class CapturingLocalM2 {\n         private final int i;\n\n         public CapturingLocalM2(int i) {\n            this.i = i;// 177\n         }// 178\n\n         public String toString() {\n            return s + \":\" + this.i;// 182\n         }\n      }\n\n      (new CapturingLocalM2(42)).toString();// 187\n      Callable<String> c1 = new Callable<String>() {\n         public String call() {\n            return null;// 192\n         }\n      };// 189\n      Callable<String> c2 = new Callable<String>() {\n         public String call() {\n            return s;// 199\n         }\n      };// 196\n      ((String)c1.call() + (String)c2.call()).length();// 203\n   }// 204\n\n   private interface Callable<T> {\n      T call();\n   }\n}\n\nclass 'pkg/PrivateClasses$1$1NonCapturingLocalR1' {\n   method '<init> (Lpkg/PrivateClasses$1;Ljava/lang/String;)V' {\n      b      11\n      e      12\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      1      15\n      4      15\n   }\n}\n\nclass 'pkg/PrivateClasses$1$1CapturingLocalR1' {\n   method '<init> (Lpkg/PrivateClasses$1;ILjava/lang/String;)V' {\n      10      25\n      13      26\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      e      29\n      14      29\n      1a      29\n      1d      29\n   }\n}\n\nclass 'pkg/PrivateClasses$1$1' {\n   method 'call ()Ljava/lang/String;' {\n      0      36\n      1      36\n   }\n}\n\nclass 'pkg/PrivateClasses$1$2' {\n   method 'call ()Ljava/lang/String;' {\n      4      41\n   }\n}\n\nclass 'pkg/PrivateClasses$1' {\n   method 'run ()V' {\n      0      5\n      2      5\n      c      19\n      15      33\n      1b      33\n      27      38\n      31      43\n      3a      44\n      3f      44\n      46      44\n      4b      44\n      51      44\n      54      44\n      58      45\n   }\n}\n\nclass 'pkg/PrivateClasses$2$1NonCapturingLocalR2' {\n   method '<init> (Lpkg/PrivateClasses$2;Ljava/lang/String;)V' {\n      b      55\n      e      56\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      1      59\n      4      59\n   }\n}\n\nclass 'pkg/PrivateClasses$2$1CapturingLocalR1' {\n   method '<init> (Lpkg/PrivateClasses$2;ILjava/lang/String;)V' {\n      10      69\n      13      70\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      e      73\n      14      73\n      1a      73\n      1d      73\n   }\n}\n\nclass 'pkg/PrivateClasses$2$1' {\n   method 'call ()Ljava/lang/String;' {\n      0      80\n      1      80\n   }\n}\n\nclass 'pkg/PrivateClasses$2$2' {\n   method 'call ()Ljava/lang/String;' {\n      4      85\n   }\n}\n\nclass 'pkg/PrivateClasses$2' {\n   method 'run ()V' {\n      0      49\n      2      49\n      c      63\n      15      77\n      1b      77\n      27      82\n      31      87\n      3a      88\n      3f      88\n      46      88\n      4b      88\n      51      88\n      54      88\n      58      89\n   }\n}\n\nclass 'pkg/PrivateClasses$1NonCapturingLocalM1' {\n   method '<init> (Ljava/lang/String;)V' {\n      6      97\n      9      98\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      1      101\n      4      101\n   }\n}\n\nclass 'pkg/PrivateClasses$1CapturingLocalM1' {\n   method '<init> (ILjava/lang/String;)V' {\n      b      111\n      e      112\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      e      115\n      14      115\n      1a      115\n      1d      115\n   }\n}\n\nclass 'pkg/PrivateClasses$3' {\n   method 'call ()Ljava/lang/String;' {\n      0      122\n      1      122\n   }\n}\n\nclass 'pkg/PrivateClasses$4' {\n   method 'call ()Ljava/lang/String;' {\n      4      127\n   }\n}\n\nclass 'pkg/PrivateClasses' {\n   method 'm1 (Ljava/lang/String;)V' {\n      8      105\n      10      119\n      16      119\n      21      124\n      2a      129\n      33      130\n      38      130\n      3f      130\n      44      130\n      4a      130\n      4d      130\n      51      131\n   }\n\n   method 'm2 (Ljava/lang/String;)V' {\n      9      146\n      12      160\n      18      160\n      24      165\n      2e      170\n      37      171\n      3c      171\n      43      171\n      48      171\n      4e      171\n      51      171\n      55      172\n   }\n}\n\nclass 'pkg/PrivateClasses$1NonCapturingLocalM2' {\n   method '<init> (Lpkg/PrivateClasses;Ljava/lang/String;)V' {\n      b      138\n      e      139\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      1      142\n      4      142\n   }\n}\n\nclass 'pkg/PrivateClasses$1CapturingLocalM2' {\n   method '<init> (Lpkg/PrivateClasses;ILjava/lang/String;)V' {\n      10      152\n      13      153\n   }\n\n   method 'toString ()Ljava/lang/String;' {\n      e      156\n      14      156\n      1a      156\n      1d      156\n   }\n}\n\nclass 'pkg/PrivateClasses$5' {\n   method 'call ()Ljava/lang/String;' {\n      0      163\n      1      163\n   }\n}\n\nclass 'pkg/PrivateClasses$6' {\n   method 'call ()Ljava/lang/String;' {\n      4      168\n   }\n}\n\nLines mapping:\n11 <-> 6\n17 <-> 12\n18 <-> 13\n22 <-> 16\n30 <-> 26\n31 <-> 27\n35 <-> 30\n39 <-> 20\n40 <-> 34\n42 <-> 39\n45 <-> 37\n49 <-> 44\n52 <-> 42\n56 <-> 45\n57 <-> 46\n63 <-> 50\n69 <-> 56\n70 <-> 57\n74 <-> 60\n82 <-> 70\n83 <-> 71\n87 <-> 74\n91 <-> 64\n92 <-> 78\n94 <-> 83\n97 <-> 81\n101 <-> 88\n104 <-> 86\n108 <-> 89\n109 <-> 90\n117 <-> 98\n118 <-> 99\n122 <-> 102\n130 <-> 112\n131 <-> 113\n135 <-> 116\n139 <-> 106\n140 <-> 120\n142 <-> 125\n145 <-> 123\n149 <-> 130\n152 <-> 128\n156 <-> 131\n157 <-> 132\n164 <-> 139\n165 <-> 140\n169 <-> 143\n177 <-> 153\n178 <-> 154\n182 <-> 157\n186 <-> 147\n187 <-> 161\n189 <-> 166\n192 <-> 164\n196 <-> 171\n199 <-> 169\n203 <-> 172\n204 <-> 173\nNot mapped:\n16\n29\n68\n81\n116\n129\n163\n176\n"
  },
  {
    "path": "testData/results/RootWithClassInner.dec",
    "content": "package sealed;\n\nsealed class RootWithClassInner {\n   static final class Inner extends RootWithClassInner {\n   }\n}\n\n"
  },
  {
    "path": "testData/results/RootWithClassOuter.dec",
    "content": "package sealed;\n\nabstract sealed class RootWithClassOuter permits ClassExtends, ClassNonSealed, ClassNonSealedExtendsImplements {\n}\n\n"
  },
  {
    "path": "testData/results/RootWithInterfaceInner.dec",
    "content": "package sealed;\n\nsealed interface RootWithInterfaceInner {\n   public static final class Inner implements RootWithInterfaceInner {\n   }\n}\n\n"
  },
  {
    "path": "testData/results/RootWithInterfaceInnerAndOuter.dec",
    "content": "package sealed;\n\nsealed interface RootWithInterfaceInnerAndOuter permits RootWithInterfaceInnerAndOuter.Inner, ClassNonSealed {\n   public static final class Inner implements RootWithInterfaceInnerAndOuter {\n   }\n}\n\n"
  },
  {
    "path": "testData/results/RootWithInterfaceOuter.dec",
    "content": "package sealed;\n\nsealed interface RootWithInterfaceOuter permits ClassImplements, InterfaceNonSealed, ClassNonSealedExtendsImplements {\n}\n\n"
  },
  {
    "path": "testData/results/RootWithModule.dec",
    "content": "package sealed.foo;\n\nimport sealed.bar.BarClassExtends;\n\npublic abstract sealed class RootWithModule permits BarClassExtends {\n}\n\n"
  },
  {
    "path": "testData/results/SwitchOnStatic.dec",
    "content": "public class SwitchOnStatic {\n   public static void main(String[] args) {\n      staticStringSelector();// 3\n      staticIntSelector();// 4\n      staticIntSelectorNotInlined();// 5\n   }// 6\n\n   public static void staticStringSelector() {\n      switch (getStaticStringSelector()) {// 9\n         case \"1\" -> System.out.println(\"a\");// 11\n         case \"2\" -> System.out.println(\"b\");// 14\n      }\n\n   }// 17\n\n   public static String getStaticStringSelector() {\n      return \"1\";// 20\n   }\n\n   public static void staticIntSelector() {\n      switch (getStaticIntSelector()) {// 24\n         case 1 -> System.out.println(\"a\");// 26\n         case 2 -> System.out.println(\"b\");// 29\n      }\n\n   }// 32\n\n   public static int getStaticIntSelector() {\n      return 1;// 35\n   }\n\n   public static void staticIntSelectorNotInlined() {\n      int cc = getStaticIntSelector();// 39\n      switch (cc) {// 40\n         case 1 -> System.out.println(\"a\");// 42\n         case 2 -> System.out.println(\"b\");// 45\n      }\n\n   }// 48\n}\n\nclass 'SwitchOnStatic' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      2\n      3      3\n      6      4\n      9      5\n   }\n\n   method 'staticStringSelector ()V' {\n      0      8\n      3e      8\n      58      9\n      5b      9\n      5d      9\n      63      10\n      66      10\n      68      10\n      6b      13\n   }\n\n   method 'getStaticStringSelector ()Ljava/lang/String;' {\n      0      16\n      2      16\n   }\n\n   method 'staticIntSelector ()V' {\n      0      20\n      3      20\n      1c      21\n      1f      21\n      21      21\n      27      22\n      2a      22\n      2c      22\n      2f      25\n   }\n\n   method 'getStaticIntSelector ()I' {\n      0      28\n      1      28\n   }\n\n   method 'staticIntSelectorNotInlined ()V' {\n      0      32\n      3      32\n      5      33\n      20      34\n      23      34\n      25      34\n      2b      35\n      2e      35\n      30      35\n      33      38\n   }\n}\n\nLines mapping:\n3 <-> 3\n4 <-> 4\n5 <-> 5\n6 <-> 6\n9 <-> 9\n11 <-> 10\n14 <-> 11\n17 <-> 14\n20 <-> 17\n24 <-> 21\n26 <-> 22\n29 <-> 23\n32 <-> 26\n35 <-> 29\n39 <-> 33\n40 <-> 34\n42 <-> 35\n45 <-> 36\n48 <-> 39\nNot mapped:\n12\n27\n43\n"
  },
  {
    "path": "testData/results/TestAbstractMethods.dec",
    "content": "package pkg;\n\npublic abstract class TestAbstractMethods {\n   public abstract void foo();\n\n   public int test(int var1) {\n      return var1;// 11\n   }\n\n   protected abstract void foo1();\n\n   public void test2(String var1) {\n      System.out.println(var1);// 17\n   }// 18\n}\n\nclass 'pkg/TestAbstractMethods' {\n   method 'test (I)I' {\n      1      6\n   }\n\n   method 'test2 (Ljava/lang/String;)V' {\n      0      12\n      4      12\n      7      13\n   }\n}\n\nLines mapping:\n11 <-> 7\n17 <-> 13\n18 <-> 14\n"
  },
  {
    "path": "testData/results/TestAccessReplace.dec",
    "content": "package pkg;\n\npublic class TestAccessReplace {\n   private static void fooS() {\n   }// 19\n\n   private void foo() {\n   }// 20\n\n   private static void fooSParams(long a, long b) {\n   }// 21\n\n   private void fooParams(long a, long b) {\n   }// 22\n\n   public class Inner {\n      public Inner(String b) {\n         TestAccessReplace.fooS();// 26\n         TestAccessReplace.this.foo();// 27\n         TestAccessReplace.fooSParams(1L, 2L);// 28\n         TestAccessReplace.this.fooParams(1L, 2L);// 29\n      }// 30\n   }\n}\n\nclass 'pkg/TestAccessReplace' {\n   method 'fooS ()V' {\n      0      4\n   }\n\n   method 'foo ()V' {\n      0      7\n   }\n\n   method 'fooSParams (JJ)V' {\n      0      10\n   }\n\n   method 'fooParams (JJ)V' {\n      0      13\n   }\n}\n\nclass 'pkg/TestAccessReplace$Inner' {\n   method '<init> (Lpkg/TestAccessReplace;Ljava/lang/String;)V' {\n      9      17\n      d      18\n      10      19\n      11      19\n      14      19\n      18      20\n      19      20\n      1c      20\n      1f      21\n   }\n}\n\nLines mapping:\n19 <-> 5\n20 <-> 8\n21 <-> 11\n22 <-> 14\n26 <-> 18\n27 <-> 19\n28 <-> 20\n29 <-> 21\n30 <-> 22\nNot mapped:\n25\n"
  },
  {
    "path": "testData/results/TestAmbiguousCall.dec",
    "content": "package pkg;\n\nclass TestAmbiguousCall {\n   void m1(RuntimeException var1, String var2) {\n   }// 4\n\n   void m1(IllegalArgumentException var1, String var2) {\n   }// 5\n\n   void test() {\n      IllegalArgumentException var1 = new IllegalArgumentException();// 8\n      this.m1((RuntimeException)var1, \"RE\");// 9\n      this.m1(var1, \"IAE\");// 10\n      IllegalArgumentException var2 = new IllegalArgumentException();// 12\n      this.m1((RuntimeException)var2, \"RE\");// 13\n      this.m1((IllegalArgumentException)var2, \"IAE\");// 14\n   }// 15\n}\n\nclass 'pkg/TestAmbiguousCall' {\n   method 'm1 (Ljava/lang/RuntimeException;Ljava/lang/String;)V' {\n      0      4\n   }\n\n   method 'm1 (Ljava/lang/IllegalArgumentException;Ljava/lang/String;)V' {\n      0      7\n   }\n\n   method 'test ()V' {\n      7      10\n      a      11\n      c      11\n      11      12\n      13      12\n      1d      13\n      20      14\n      22      14\n      27      15\n      2a      15\n      2c      15\n      2f      16\n   }\n}\n\nLines mapping:\n4 <-> 5\n5 <-> 8\n8 <-> 11\n9 <-> 12\n10 <-> 13\n12 <-> 14\n13 <-> 15\n14 <-> 16\n15 <-> 17\n"
  },
  {
    "path": "testData/results/TestAmbiguousCallWithDebugInfo.dec",
    "content": "package pkg;\n\nclass TestAmbiguousCall {\n   void m1(RuntimeException e, String s) {\n   }// 4\n\n   void m1(IllegalArgumentException e, String s) {\n   }// 5\n\n   void test() {\n      IllegalArgumentException iae = new IllegalArgumentException();// 8\n      this.m1((RuntimeException)iae, \"RE\");// 9\n      this.m1(iae, \"IAE\");// 10\n      RuntimeException re = new IllegalArgumentException();// 12\n      this.m1((RuntimeException)re, \"RE\");// 13\n      this.m1((IllegalArgumentException)re, \"IAE\");// 14\n   }// 15\n}\n\nclass 'pkg/TestAmbiguousCall' {\n   method 'm1 (Ljava/lang/RuntimeException;Ljava/lang/String;)V' {\n      0      4\n   }\n\n   method 'm1 (Ljava/lang/IllegalArgumentException;Ljava/lang/String;)V' {\n      0      7\n   }\n\n   method 'test ()V' {\n      7      10\n      a      11\n      c      11\n      11      12\n      13      12\n      1d      13\n      20      14\n      22      14\n      27      15\n      2a      15\n      2c      15\n      2f      16\n   }\n}\n\nLines mapping:\n4 <-> 5\n5 <-> 8\n8 <-> 11\n9 <-> 12\n10 <-> 13\n12 <-> 14\n13 <-> 15\n14 <-> 16\n15 <-> 17\n"
  },
  {
    "path": "testData/results/TestAnonymousClass.dec",
    "content": "package pkg;\n\npublic abstract class TestAnonymousClass {\n   public static final Runnable R3 = new Runnable() {\n      public void run() {\n         boolean var1 = true;// 28\n         boolean var2 = true;// 29\n      }// 30\n   };\n   public static final Runnable R = new Runnable() {\n      public void run() {\n         boolean var1 = true;// 45\n         boolean var2 = true;// 46\n      }// 47\n   };\n   public static final Runnable R1 = new Runnable() {\n      public void run() {\n         boolean var1 = true;// 53\n         boolean var2 = true;// 54\n      }// 55\n   };\n   private final InnerRecursive y = new InnerRecursive(new InnerRecursive((InnerRecursive)null) {\n      void foo() {\n         boolean var1 = true;// 75\n         boolean var2 = true;// 76\n         boolean var3 = true;// 77\n      }// 78\n   }) {\n      int v = 5;\n      int t = 5;\n      int j = 5;\n      int o = 5;\n   };\n   private final InnerRecursive x = new InnerRecursive(new InnerRecursive((InnerRecursive)null) {\n      void foo() {\n         boolean var1 = true;// 90\n         boolean var2 = true;// 91\n         boolean var3 = true;// 92\n      }// 93\n   }) {\n      int v = 5;\n      int t = 5;\n      int j = 5;\n      int o = 5;\n   };\n\n   void foo(int var1) throws Exception {\n      if (var1 > 0) {// 10\n         I var2 = new I() {\n            public void foo() throws Exception {\n               boolean var1 = true;// 13\n               boolean var2 = true;// 14\n            }// 15\n         };// 11\n         var2.foo();// 17\n      } else {\n         System.out.println(5);// 21\n      }\n\n   }// 23\n\n   void boo() {\n      boolean var1 = true;// 35\n   }// 36\n\n   void zoo() {\n      boolean var1 = true;// 39\n   }// 40\n\n   static class InnerRecursive {\n      InnerRecursive r;\n\n      public InnerRecursive(InnerRecursive var1) {\n         this.r = var1;// 105\n      }// 106\n\n      void foo() {\n      }// 110\n   }\n\n   private static class Inner {\n      private static Runnable R_I = new Runnable() {\n         public void run() {\n            boolean var1 = true;// 66\n            boolean var2 = true;// 67\n         }// 68\n      };\n   }\n\n   interface I {\n      void foo() throws Exception;\n   }\n}\n\nclass 'pkg/TestAnonymousClass$2' {\n   method 'run ()V' {\n      0      5\n      1      5\n      2      6\n      3      6\n      4      7\n   }\n}\n\nclass 'pkg/TestAnonymousClass$3' {\n   method 'run ()V' {\n      0      11\n      1      11\n      2      12\n      3      12\n      4      13\n   }\n}\n\nclass 'pkg/TestAnonymousClass$4' {\n   method 'run ()V' {\n      0      17\n      1      17\n      2      18\n      3      18\n      4      19\n   }\n}\n\nclass 'pkg/TestAnonymousClass$5' {\n   method 'foo ()V' {\n      0      23\n      1      23\n      2      24\n      3      24\n      4      25\n      5      25\n      6      26\n   }\n}\n\nclass 'pkg/TestAnonymousClass$7' {\n   method 'foo ()V' {\n      0      35\n      1      35\n      2      36\n      3      36\n      4      37\n      5      37\n      6      38\n   }\n}\n\nclass 'pkg/TestAnonymousClass$1' {\n   method 'foo ()V' {\n      0      50\n      1      50\n      2      51\n      3      51\n      4      52\n   }\n}\n\nclass 'pkg/TestAnonymousClass' {\n   method 'foo (I)V' {\n      1      47\n      c      53\n      e      54\n      16      56\n      19      56\n      1a      56\n      1d      59\n   }\n\n   method 'boo ()V' {\n      0      62\n      1      62\n      2      63\n   }\n\n   method 'zoo ()V' {\n      0      66\n      1      66\n      2      67\n   }\n}\n\nclass 'pkg/TestAnonymousClass$InnerRecursive' {\n   method '<init> (Lpkg/TestAnonymousClass$InnerRecursive;)V' {\n      6      73\n      9      74\n   }\n\n   method 'foo ()V' {\n      0      77\n   }\n}\n\nclass 'pkg/TestAnonymousClass$Inner$1' {\n   method 'run ()V' {\n      0      83\n      1      83\n      2      84\n      3      84\n      4      85\n   }\n}\n\nLines mapping:\n10 <-> 48\n11 <-> 54\n13 <-> 51\n14 <-> 52\n15 <-> 53\n17 <-> 55\n21 <-> 57\n23 <-> 60\n28 <-> 6\n29 <-> 7\n30 <-> 8\n35 <-> 63\n36 <-> 64\n39 <-> 67\n40 <-> 68\n45 <-> 12\n46 <-> 13\n47 <-> 14\n53 <-> 18\n54 <-> 19\n55 <-> 20\n66 <-> 84\n67 <-> 85\n68 <-> 86\n75 <-> 24\n76 <-> 25\n77 <-> 26\n78 <-> 27\n90 <-> 36\n91 <-> 37\n92 <-> 38\n93 <-> 39\n105 <-> 74\n106 <-> 75\n110 <-> 78\nNot mapped:\n18\n104\n"
  },
  {
    "path": "testData/results/TestAnonymousClassConstructor.dec",
    "content": "package pkg;\n\nclass TestAnonymousClassConstructor {\n   void innerPrivateString() {\n      InnerPrivateString var10001 = new InnerPrivateString(\"text\") {// 5\n      };\n   }// 6\n\n   void innerPrivate() {\n      InnerPrivate var10001 = new InnerPrivate(3L, 4) {// 9\n      };\n   }// 10\n\n   void innerStaticPrivateString() {\n      InnerStaticPrivateString var10001 = new InnerStaticPrivateString(\"text\") {// 13\n      };\n   }// 14\n\n   void innerStaticPrivate() {\n      InnerStaticPrivate var10001 = new InnerStaticPrivate(3L, 4) {// 17\n      };\n   }// 18\n\n   static void innerStaticPrivateStringStatic() {\n      InnerStaticPrivateString var10001 = new InnerStaticPrivateString(\"text\") {// 21\n      };\n   }// 22\n\n   static void innerStaticPrivateStatic() {\n      InnerStaticPrivate var10001 = new InnerStaticPrivate(3L, 4) {// 25\n      };\n   }// 26\n\n   void innerPublicString() {\n      InnerPublicString var10001 = new InnerPublicString(\"text\") {// 29\n      };\n   }// 30\n\n   void innerPublic() {\n      InnerPublic var10001 = new InnerPublic(3L, 4) {// 33\n      };\n   }// 34\n\n   void innerStaticPublicString() {\n      InnerStaticPublicString var10001 = new InnerStaticPublicString(\"text\") {// 37\n      };\n   }// 38\n\n   void innerStaticPublic() {\n      InnerStaticPublic var10001 = new InnerStaticPublic(3L, 4) {// 41\n      };\n   }// 42\n\n   static void innerStaticPublicStringStatic() {\n      InnerStaticPublicString var10001 = new InnerStaticPublicString(\"text\") {// 45\n      };\n   }// 46\n\n   static void innerStaticPublicStatic() {\n      InnerStaticPublic var10001 = new InnerStaticPublic(3L, 4) {// 49\n      };\n   }// 50\n\n   static void n(String s) {\n      System.out.println(\"n(): \" + s);// 53\n   }// 54\n\n   static class InnerStaticPublic {\n      public InnerStaticPublic(long a, int b) {\n         TestAnonymousClassConstructor.n(a + \"+\" + b);// 100\n      }// 101\n   }\n\n   static class InnerStaticPublicString {\n      public InnerStaticPublicString(String s) {\n         TestAnonymousClassConstructor.n(s);// 94\n      }// 95\n   }\n\n   class InnerPublic {\n      public InnerPublic(long a, int b) {\n         TestAnonymousClassConstructor.n(a + \"+\" + b);// 88\n      }// 89\n   }\n\n   class InnerPublicString {\n      public InnerPublicString(String s) {\n         TestAnonymousClassConstructor.n(s);// 82\n      }// 83\n   }\n\n   static class InnerStaticPrivate {\n      private InnerStaticPrivate(long a, int b) {\n         TestAnonymousClassConstructor.n(a + \"+\" + b);// 76\n      }// 77\n   }\n\n   static class InnerStaticPrivateString {\n      private InnerStaticPrivateString(String s) {\n         TestAnonymousClassConstructor.n(s);// 70\n      }// 71\n   }\n\n   class InnerPrivate {\n      private InnerPrivate(long a, int b) {\n         TestAnonymousClassConstructor.n(a + \"+\" + b);// 64\n      }// 65\n   }\n\n   class InnerPrivateString {\n      private InnerPrivateString(String s) {\n         TestAnonymousClassConstructor.n(s);// 58\n      }// 59\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor' {\n   method 'innerPrivateString ()V' {\n      5      4\n      b      6\n   }\n\n   method 'innerPrivate ()V' {\n      5      9\n      8      9\n      d      11\n   }\n\n   method 'innerStaticPrivateString ()V' {\n      5      14\n      b      16\n   }\n\n   method 'innerStaticPrivate ()V' {\n      5      19\n      8      19\n      d      21\n   }\n\n   method 'innerStaticPrivateStringStatic ()V' {\n      4      24\n      a      26\n   }\n\n   method 'innerStaticPrivateStatic ()V' {\n      4      29\n      7      29\n      c      31\n   }\n\n   method 'innerPublicString ()V' {\n      5      34\n      b      36\n   }\n\n   method 'innerPublic ()V' {\n      5      39\n      8      39\n      d      41\n   }\n\n   method 'innerStaticPublicString ()V' {\n      5      44\n      b      46\n   }\n\n   method 'innerStaticPublic ()V' {\n      5      49\n      8      49\n      d      51\n   }\n\n   method 'innerStaticPublicStringStatic ()V' {\n      4      54\n      a      56\n   }\n\n   method 'innerStaticPublicStatic ()V' {\n      4      59\n      7      59\n      c      61\n   }\n\n   method 'n (Ljava/lang/String;)V' {\n      0      64\n      a      64\n      13      64\n      16      64\n      19      65\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor$InnerStaticPublic' {\n   method '<init> (JI)V' {\n      f      69\n      18      69\n      1b      69\n      1e      70\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor$InnerStaticPublicString' {\n   method '<init> (Ljava/lang/String;)V' {\n      5      75\n      8      76\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor$InnerPublic' {\n   method '<init> (Lpkg/TestAnonymousClassConstructor;JI)V' {\n      14      81\n      1e      81\n      21      81\n      24      82\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor$InnerPublicString' {\n   method '<init> (Lpkg/TestAnonymousClassConstructor;Ljava/lang/String;)V' {\n      a      87\n      d      88\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor$InnerStaticPrivate' {\n   method '<init> (JI)V' {\n      f      93\n      18      93\n      1b      93\n      1e      94\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor$InnerStaticPrivateString' {\n   method '<init> (Ljava/lang/String;)V' {\n      5      99\n      8      100\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor$InnerPrivate' {\n   method '<init> (Lpkg/TestAnonymousClassConstructor;JI)V' {\n      14      105\n      1e      105\n      21      105\n      24      106\n   }\n}\n\nclass 'pkg/TestAnonymousClassConstructor$InnerPrivateString' {\n   method '<init> (Lpkg/TestAnonymousClassConstructor;Ljava/lang/String;)V' {\n      a      111\n      d      112\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 7\n9 <-> 10\n10 <-> 12\n13 <-> 15\n14 <-> 17\n17 <-> 20\n18 <-> 22\n21 <-> 25\n22 <-> 27\n25 <-> 30\n26 <-> 32\n29 <-> 35\n30 <-> 37\n33 <-> 40\n34 <-> 42\n37 <-> 45\n38 <-> 47\n41 <-> 50\n42 <-> 52\n45 <-> 55\n46 <-> 57\n49 <-> 60\n50 <-> 62\n53 <-> 65\n54 <-> 66\n58 <-> 112\n59 <-> 113\n64 <-> 106\n65 <-> 107\n70 <-> 100\n71 <-> 101\n76 <-> 94\n77 <-> 95\n82 <-> 88\n83 <-> 89\n88 <-> 82\n89 <-> 83\n94 <-> 76\n95 <-> 77\n100 <-> 70\n101 <-> 71\nNot mapped:\n57\n63\n69\n75\n81\n87\n93\n99\n"
  },
  {
    "path": "testData/results/TestAnonymousParamNames.dec",
    "content": "package pkg;\n\npublic class TestAnonymousParamNames {\n   private final Clazz reference = new Clazz(0L, false) {\n   };\n\n   private class Clazz {\n      public Clazz(long paramL, boolean paramB) {\n      }// 25\n   }\n}\n\nclass 'pkg/TestAnonymousParamNames$Clazz' {\n   method '<init> (Lpkg/TestAnonymousParamNames;JZ)V' {\n      9      8\n   }\n}\n\nLines mapping:\n25 <-> 9\nNot mapped:\n24\n"
  },
  {
    "path": "testData/results/TestAnonymousParams.dec",
    "content": "package pkg;\n\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class TestAnonymousParams {\n   void foo(InputStream in, final int a) throws IOException {\n      FilterInputStream filterInputStream = new FilterInputStream(in) {\n         public int read() throws IOException {\n            return a;// 27\n         }\n      };// 24\n      filterInputStream.read();// 30\n   }// 31\n}\n\nclass 'pkg/TestAnonymousParams$1' {\n   method 'read ()I' {\n      4      10\n   }\n}\n\nclass 'pkg/TestAnonymousParams' {\n   method 'foo (Ljava/io/InputStream;I)V' {\n      a      12\n      c      13\n      10      14\n   }\n}\n\nLines mapping:\n24 <-> 13\n27 <-> 11\n30 <-> 14\n31 <-> 15\n"
  },
  {
    "path": "testData/results/TestAnonymousSignature.dec",
    "content": "package pkg;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\n\npublic class TestAnonymousSignature {\n   public static void main(String[] var0) {\n      System.out.println(new ArrayList<String>() {// 25\n         public int size() {\n            return super.size();// 28\n         }\n      });\n      System.out.println(new Comparator<String>() {// 33\n         public int compare(String var1, String var2) {\n            return 0;// 36\n         }\n      });\n   }// 39\n}\n\nclass 'pkg/TestAnonymousSignature$1' {\n   method 'size ()I' {\n      1      9\n      4      9\n   }\n}\n\nclass 'pkg/TestAnonymousSignature$2' {\n   method 'compare (Ljava/lang/String;Ljava/lang/String;)I' {\n      0      14\n      1      14\n   }\n}\n\nclass 'pkg/TestAnonymousSignature' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      7\n      a      7\n      d      12\n      17      12\n      1a      17\n   }\n}\n\nLines mapping:\n25 <-> 8\n28 <-> 10\n33 <-> 13\n36 <-> 15\n39 <-> 18\n"
  },
  {
    "path": "testData/results/TestAsserts.dec",
    "content": "package pkg;\n\npublic class TestAsserts {\n   public static int foo() {\n      byte var0 = 1;// 21\n\n      assert var0 > 1;// 22\n\n      assert var0 > 1 && var0 < 5;// 23\n\n      return 1;// 24\n   }\n}\n\nclass 'pkg/TestAsserts' {\n   method 'foo ()I' {\n      0      4\n      1      4\n      9      6\n      a      6\n      1c      8\n      1d      8\n      21      8\n      22      8\n      2d      10\n      2e      10\n   }\n}\n\nLines mapping:\n21 <-> 5\n22 <-> 7\n23 <-> 9\n24 <-> 11\n"
  },
  {
    "path": "testData/results/TestClashName.dec",
    "content": "package pkg;\n\n@SharedName4\npublic class TestClashName extends ext.TestClashNameParent implements TestClashNameIface {\n   int TestClashNameParent = 0;\n   int TestClashNameIface = 0;\n   int SharedName1 = 0;\n   int SharedName4 = 0;\n   int SharedName5 = 0;\n   int i;\n   int j;\n   int k;\n   int l;\n   int m;\n   int n;\n   SharedName1 p;\n   SharedName5<SharedName1> q;\n\n   public TestClashName() {\n      this.i = pkg.SharedName1.f;// 59\n      this.j = NonSharedName.f;// 60\n      this.k = SharedName2.f;// 61\n      this.l = pkg.SharedName3.f;// 62\n      this.m = pkg.SharedName1.getF();// 63\n      this.n = NonSharedName.getF();// 64\n      this.p = null;// 65\n      this.q = null;// 67\n   }\n\n   @SharedName4\n   public int m() {\n      int var1 = this.i;// 73\n      pkg.SharedName1.f = this.j;// 74\n      int var2 = SharedName2.f;// 75\n      NonSharedName.f = this.k;// 76\n      int var3 = NonSharedName.f;// 77\n      return var1 + var2 + var3;// 78\n   }\n\n   public void f() {\n   }// 82\n}\n\nclass 'pkg/TestClashName' {\n   method '<init> ()V' {\n      1e      19\n      21      19\n      25      20\n      28      20\n      2c      21\n      2f      21\n      33      22\n      36      22\n      3a      23\n      3d      23\n      41      24\n      44      24\n      48      25\n      49      25\n      4d      26\n      4e      26\n      51      27\n   }\n\n   method 'm ()I' {\n      1      31\n      4      31\n      6      32\n      9      32\n      c      33\n      f      33\n      11      34\n      14      34\n      17      35\n      1a      35\n      1d      36\n      1f      36\n      20      36\n   }\n\n   method 'f ()V' {\n      0      40\n   }\n}\n\nLines mapping:\n59 <-> 20\n60 <-> 21\n61 <-> 22\n62 <-> 23\n63 <-> 24\n64 <-> 25\n65 <-> 26\n67 <-> 27\n73 <-> 32\n74 <-> 33\n75 <-> 34\n76 <-> 35\n77 <-> 36\n78 <-> 37\n82 <-> 41\nNot mapped:\n52\n53\n54\n55\n56\n57\n"
  },
  {
    "path": "testData/results/TestClassCast.dec",
    "content": "package pkg;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TestClassCast {\n   public void test(List var1) {\n      Object var2 = var1;// 22\n      if (var1 != null) {// 23\n         ((List)(var2 = new ArrayList(var1))).add(\"23\");// 24\n      }\n\n      System.out.println(((List)var2).size());// 26\n   }// 27\n}\n\nclass 'pkg/TestClassCast' {\n   method 'test (Ljava/util/List;)V' {\n      1      7\n      3      8\n      f      9\n      10      9\n      12      9\n      18      12\n      1c      12\n      21      12\n      24      13\n   }\n}\n\nLines mapping:\n22 <-> 8\n23 <-> 9\n24 <-> 10\n26 <-> 13\n27 <-> 14\n"
  },
  {
    "path": "testData/results/TestClassFields.dec",
    "content": "package pkg;\n\npublic class TestClassFields {\n   private static int[] sizes;\n   private static String[] names = new String[]{\"name1\", \"name2\"};\n   private static final int SIZE;\n\n   static {\n      sizes = new int[names.length];// 15\n      TestClassFields.Inner.staticMutable = 3;// 17\n      SIZE = TestClassFields.Inner.staticMutable;// 18\n   }// 19\n\n   private static class Inner {\n      private static int staticMutable;\n   }\n}\n\nclass 'pkg/TestClassFields' {\n   method '<clinit> ()V' {\n      11      8\n      14      8\n      17      8\n      1b      9\n      1f      10\n      22      10\n      25      11\n   }\n}\n\nLines mapping:\n15 <-> 9\n17 <-> 10\n18 <-> 11\n19 <-> 12\nNot mapped:\n14\n"
  },
  {
    "path": "testData/results/TestClassLambda.dec",
    "content": "package pkg;\n\nimport java.lang.annotation.Annotation;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.OptionalInt;\nimport java.util.function.IntBinaryOperator;\nimport java.util.function.Supplier;\n\npublic class TestClassLambda {\n   public int field = 0;\n\n   public void testLambda() {\n      List var1 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);// 29\n      int var2 = (int)Math.random();// 30\n      var1.forEach((var2x) -> {// 32\n         int var3 = 2 * var2x;// 33\n         System.out.println(var3 + var2 + this.field);// 34\n      });// 35\n   }// 36\n\n   public void testLambda1() {\n      int var1 = (int)Math.random();// 39\n      Runnable var2 = () -> {\n         System.out.println(\"hello1\" + var1);\n      };// 40\n      Runnable var3 = () -> {\n         System.out.println(\"hello2\" + var1);\n      };// 41\n   }// 42\n\n   public void testLambda2() {\n      reduce((var0, var1) -> {// 45\n         return Math.max(var0, var1);\n      });\n   }// 46\n\n   public void testLambda3() {\n      reduce(Math::max);// 49\n   }// 50\n\n   public void testLambda4() {\n      reduce(TestClassLambda::localMax);// 53\n   }// 54\n\n   public void testLambda5() {\n      String var1 = \"abcd\";// 57\n      function(var1::toString);// 58\n   }// 59\n\n   public void testLambda6() {\n      ArrayList var1 = new ArrayList();// 62\n      int var2 = var1.size() * 2;// 63\n      int var3 = var1.size() * 5;// 64\n      var1.removeIf((var2x) -> {// 65\n         return var2 >= var2x.length() && var2x.length() <= var3;\n      });\n   }// 66\n\n   public static void testLambda7(Annotation[] var0) {\n      Arrays.stream(var0).map(Annotation::annotationType);// 69\n   }// 70\n\n   public static OptionalInt reduce(IntBinaryOperator var0) {\n      return null;// 73\n   }\n\n   public static String function(Supplier<String> var0) {\n      return (String)var0.get();// 77\n   }\n\n   public static int localMax(int var0, int var1) {\n      return 0;// 81\n   }\n\n   public void nestedLambdas() {\n      byte var1 = 5;// 85\n      Runnable var2 = () -> {\n         Runnable var1x = () -> {\n            System.out.println(\"hello2\" + var1);\n         };// 87\n         System.out.println(\"hello1\" + var1);// 88\n      };// 86 89\n   }// 90\n}\n\nclass 'pkg/TestClassLambda' {\n   method 'lambda$testLambda$0 (ILjava/lang/Integer;)V' {\n      0      17\n      2      17\n      5      17\n      6      17\n      7      18\n      c      18\n      e      18\n      11      18\n      12      18\n      15      19\n   }\n\n   method 'testLambda ()V' {\n      7      14\n      8      14\n      e      14\n      f      14\n      15      14\n      16      14\n      1c      14\n      1d      14\n      23      14\n      24      14\n      2a      14\n      2c      14\n      33      14\n      35      14\n      39      14\n      3c      14\n      3d      15\n      40      15\n      41      15\n      4a      16\n      4f      20\n   }\n\n   method 'lambda$testLambda1$1 (I)V' {\n      0      25\n      a      25\n      13      25\n      16      25\n      19      26\n   }\n\n   method 'lambda$testLambda1$2 (I)V' {\n      0      28\n      a      28\n      13      28\n      16      28\n      19      29\n   }\n\n   method 'testLambda1 ()V' {\n      0      23\n      3      23\n      4      23\n      b      26\n      12      29\n      13      30\n   }\n\n   method 'lambda$testLambda2$3 (II)I' {\n      2      34\n      5      34\n   }\n\n   method 'testLambda2 ()V' {\n      5      33\n      9      36\n   }\n\n   method 'testLambda3 ()V' {\n      5      39\n      9      40\n   }\n\n   method 'testLambda4 ()V' {\n      5      43\n      9      44\n   }\n\n   method 'testLambda5 ()V' {\n      0      47\n      2      47\n      e      48\n      12      49\n   }\n\n   method 'lambda$testLambda6$4 (IILjava/lang/String;)Z' {\n      2      56\n      5      56\n      9      56\n      d      56\n      15      56\n   }\n\n   method 'testLambda6 ()V' {\n      7      52\n      9      53\n      e      53\n      f      53\n      10      53\n      12      54\n      17      54\n      18      54\n      19      54\n      22      55\n      28      58\n   }\n\n   method 'testLambda7 ([Ljava/lang/annotation/Annotation;)V' {\n      1      61\n      9      61\n      f      62\n   }\n\n   method 'reduce (Ljava/util/function/IntBinaryOperator;)Ljava/util/OptionalInt;' {\n      0      65\n      1      65\n   }\n\n   method 'function (Ljava/util/function/Supplier;)Ljava/lang/String;' {\n      1      69\n      6      69\n      9      69\n   }\n\n   method 'localMax (II)I' {\n      0      73\n      1      73\n   }\n\n   method 'lambda$null$5 (I)V' {\n      0      80\n      a      80\n      13      80\n      16      80\n      19      81\n   }\n\n   method 'lambda$nestedLambdas$6 (I)V' {\n      6      81\n      7      82\n      11      82\n      1a      82\n      1d      82\n      20      83\n   }\n\n   method 'nestedLambdas ()V' {\n      0      77\n      1      77\n      8      83\n      9      84\n   }\n}\n\nLines mapping:\n29 <-> 15\n30 <-> 16\n32 <-> 17\n33 <-> 18\n34 <-> 19\n35 <-> 20\n36 <-> 21\n39 <-> 24\n40 <-> 27\n41 <-> 30\n42 <-> 31\n45 <-> 34\n46 <-> 37\n49 <-> 40\n50 <-> 41\n53 <-> 44\n54 <-> 45\n57 <-> 48\n58 <-> 49\n59 <-> 50\n62 <-> 53\n63 <-> 54\n64 <-> 55\n65 <-> 56\n66 <-> 59\n69 <-> 62\n70 <-> 63\n73 <-> 66\n77 <-> 70\n81 <-> 74\n85 <-> 78\n86 <-> 84\n87 <-> 82\n88 <-> 83\n89 <-> 84\n90 <-> 85\n"
  },
  {
    "path": "testData/results/TestClassLoop.dec",
    "content": "package pkg;\n\npublic class TestClassLoop {\n   public static void testSimpleInfinite() {\n      while(true) {\n         System.out.println();// 23\n      }\n   }\n\n   public static void testFinally() {\n      boolean var0 = Math.random() > 0.0;// 29\n\n      while(true) {\n         try {\n            if (!var0) {// 33\n               return;// 34\n            }\n         } finally {\n            System.out.println(\"1\");// 38\n         }\n      }\n   }\n\n   public static void testFinallyContinue() {\n      boolean var0 = Math.random() > 0.0;// 45\n\n      while(true) {\n         while(true) {\n            try {\n               System.out.println(\"1\");// 49\n               break;\n            } finally {\n               if (var0) {// 52\n                  System.out.println(\"3\");// 53\n                  continue;// 54\n               }\n            }\n         }\n\n         System.out.println(\"4\");// 58\n      }\n   }\n\n   public static int testWhileCombined(String var0) {\n      int var1 = var0.length();// 63\n      int var2 = 0;// 64\n      boolean var3 = false;// 65\n      boolean var4 = false;// 66\n      int var5 = 0;// 67\n      int var7 = 0;// 69\n\n      for(boolean var8 = false; var2 < var1; ++var2) {// 70 73 90\n         char var6 = var0.charAt(var2);// 74\n         if (var6 == '0') {// 75\n            ++var7;// 76\n         } else {\n            if (var6 != '.') {// 77\n               break;\n            }\n\n            if (var3) {// 78\n               throw new NumberFormatException(\"multiple points\");// 80\n            }\n\n            var5 = var2;// 82\n            if (var4) {// 83\n               var5 = var2 - 1;// 84\n            }\n\n            var3 = true;// 86\n         }\n      }\n\n      return var5;// 92\n   }\n}\n\nclass 'pkg/TestClassLoop' {\n   method 'testSimpleInfinite ()V' {\n      0      5\n      3      5\n   }\n\n   method 'testFinally ()V' {\n      0      10\n      3      10\n      4      10\n      d      10\n      f      14\n      1a      15\n      26      18\n      27      18\n      2a      18\n   }\n\n   method 'testFinallyContinue ()V' {\n      0      24\n      3      24\n      4      24\n      d      24\n      e      29\n      11      29\n      13      29\n      26      32\n      2a      33\n      2d      33\n      2f      33\n      32      34\n      37      39\n      3a      39\n      3c      39\n   }\n\n   method 'testWhileCombined (Ljava/lang/String;)I' {\n      1      44\n      4      44\n      5      45\n      6      45\n      7      46\n      8      46\n      9      47\n      a      47\n      c      48\n      d      48\n      f      49\n      10      49\n      12      51\n      13      51\n      17      51\n      1c      52\n      1f      52\n      23      53\n      25      53\n      28      54\n      30      56\n      32      56\n      36      60\n      3d      61\n      42      61\n      44      64\n      48      65\n      4b      66\n      4e      69\n      4f      69\n      50      51\n      58      73\n   }\n}\n\nLines mapping:\n23 <-> 6\n29 <-> 11\n33 <-> 15\n34 <-> 16\n38 <-> 19\n45 <-> 25\n49 <-> 30\n52 <-> 33\n53 <-> 34\n54 <-> 35\n58 <-> 40\n63 <-> 45\n64 <-> 46\n65 <-> 47\n66 <-> 48\n67 <-> 49\n69 <-> 50\n70 <-> 52\n73 <-> 52\n74 <-> 53\n75 <-> 54\n76 <-> 55\n77 <-> 57\n78 <-> 61\n80 <-> 62\n82 <-> 65\n83 <-> 66\n84 <-> 67\n86 <-> 70\n90 <-> 52\n92 <-> 74\nNot mapped:\n39\n"
  },
  {
    "path": "testData/results/TestClassNestedInitializer.dec",
    "content": "package pkg;\n\npublic class TestClassNestedInitializer {\n   public String secret;\n\n   public void test() {\n      TestClassNestedInitializer var1 = new TestClassNestedInitializer() {\n         {\n            this.secret = \"one\";\n         }\n      };// 22\n      System.out.println(var1.secret);// 23\n   }// 24\n}\n\nclass 'pkg/TestClassNestedInitializer$1' {\n   method '<init> (Lpkg/TestClassNestedInitializer;)V' {\n      a      8\n      c      8\n      f      9\n   }\n}\n\nclass 'pkg/TestClassNestedInitializer' {\n   method 'test ()V' {\n      8      10\n      9      11\n      d      11\n      10      11\n      13      12\n   }\n}\n\nLines mapping:\n22 <-> 11\n23 <-> 12\n24 <-> 13\n"
  },
  {
    "path": "testData/results/TestClassSimpleBytecodeMapping.dec",
    "content": "package pkg;\n\npublic class TestClassSimpleBytecodeMapping {\n   public int test() {\n      System.out.println(\"before\");// 12\n      this.run(new Runnable() {// 14\n         public void run() {\n            System.out.println(\"Runnable\");// 17\n         }// 18\n      });\n      this.test2(\"1\");// 21\n      if (Math.random() > 0.0) {// 23\n         System.out.println(\"0\");// 24\n         return 0;// 25\n      } else {\n         System.out.println(\"1\");// 27\n         return 1;// 28\n      }\n   }\n\n   public void test2(String var1) {\n      try {\n         Integer.parseInt(var1);// 34\n      } catch (Exception var6) {// 35\n         System.out.println(var6);// 36\n      } finally {\n         System.out.println(\"Finally\");// 38\n      }\n\n   }// 40\n\n   void run(Runnable var1) {\n      var1.run();// 49\n   }// 50\n\n   public class InnerClass2 {\n      public void print() {\n         System.out.println(\"Inner2\");// 54\n      }// 55\n   }\n\n   public class InnerClass {\n      public void print() {\n         System.out.println(\"Inner\");// 44\n      }// 45\n   }\n}\n\nclass 'pkg/TestClassSimpleBytecodeMapping$1' {\n   method 'run ()V' {\n      0      7\n      3      7\n      5      7\n      8      8\n   }\n}\n\nclass 'pkg/TestClassSimpleBytecodeMapping' {\n   method 'test ()I' {\n      0      4\n      3      4\n      5      4\n      11      5\n      15      10\n      17      10\n      1a      11\n      1d      11\n      1e      11\n      1f      11\n      22      12\n      25      12\n      27      12\n      2a      13\n      2b      13\n      2c      15\n      2f      15\n      31      15\n      34      16\n      35      16\n   }\n\n   method 'test2 (Ljava/lang/String;)V' {\n      1      22\n      10      23\n      11      24\n      15      24\n      23      26\n      24      26\n      27      26\n      2e      29\n   }\n\n   method 'run (Ljava/lang/Runnable;)V' {\n      1      32\n      6      33\n   }\n}\n\nclass 'pkg/TestClassSimpleBytecodeMapping$InnerClass2' {\n   method 'print ()V' {\n      0      37\n      3      37\n      5      37\n      8      38\n   }\n}\n\nclass 'pkg/TestClassSimpleBytecodeMapping$InnerClass' {\n   method 'print ()V' {\n      0      43\n      3      43\n      5      43\n      8      44\n   }\n}\n\nLines mapping:\n12 <-> 5\n14 <-> 6\n17 <-> 8\n18 <-> 9\n21 <-> 11\n23 <-> 12\n24 <-> 13\n25 <-> 14\n27 <-> 16\n28 <-> 17\n34 <-> 23\n35 <-> 24\n36 <-> 25\n38 <-> 27\n40 <-> 30\n44 <-> 44\n45 <-> 45\n49 <-> 33\n50 <-> 34\n54 <-> 38\n55 <-> 39\nNot mapped:\n39\n"
  },
  {
    "path": "testData/results/TestClassSwitch.dec",
    "content": "package pkg;\n\npublic class TestClassSwitch {\n   public void testCaseOrder(int var1) {\n      switch (var1) {// 22\n         case 5:\n            System.out.println(5);// 27\n         default:\n            return;// 29\n         case 13:\n            System.out.println(13);// 24\n      }\n   }// 25\n}\n\nclass 'pkg/TestClassSwitch' {\n   method 'testCaseOrder (I)V' {\n      1      4\n      1c      10\n      1f      10\n      21      10\n      24      12\n      25      6\n      28      6\n      29      6\n      2c      8\n   }\n}\n\nLines mapping:\n22 <-> 5\n24 <-> 11\n25 <-> 13\n27 <-> 7\n29 <-> 9\n"
  },
  {
    "path": "testData/results/TestClassTypes.dec",
    "content": "package pkg;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TestClassTypes {\n   public void testBoolean() {\n      byte var1 = 0;// 25\n      long var2 = System.currentTimeMillis();// 26\n      if (var2 % 2L > 0L) {// 28\n         var1 = 1;// 29\n      } else if (var2 % 3L > 0L) {// 31\n         var1 = 2;// 32\n      }\n\n      if (var1 == 1) {// 35\n         System.out.println();// 36\n      }\n\n   }// 38\n\n   public boolean testBit(int var1) {\n      return (var1 & 1) == 1;// 41\n   }\n\n   public void testSwitchConsts(int var1) {\n      switch (var1) {// 46\n         case 88:\n            System.out.println(\"1\");// 48\n            break;// 49\n         case 656:\n            System.out.println(\"2\");// 51\n            break;// 52\n         case 65201:\n         case 65489:\n            System.out.println(\"3\");// 55\n      }\n\n   }// 57\n\n   public void testAssignmentType(List var1) {\n      Object var2 = var1;// 61\n      if (var1 != null) {// 63\n         ((List)(var2 = new ArrayList(var1))).add(\"23\");// 64\n      }\n\n      System.out.println(((List)var2).size());// 67\n   }// 68\n}\n\nclass 'pkg/TestClassTypes' {\n   method 'testBoolean ()V' {\n      0      7\n      1      7\n      2      8\n      5      8\n      7      9\n      a      9\n      b      9\n      c      9\n      d      9\n      10      10\n      11      10\n      16      11\n      19      11\n      1a      11\n      1b      11\n      1c      11\n      1f      12\n      20      12\n      22      15\n      23      15\n      26      16\n      29      16\n      2c      19\n   }\n\n   method 'testBit (I)Z' {\n      1      22\n      2      22\n      3      22\n      4      22\n      c      22\n   }\n\n   method 'testSwitchConsts (I)V' {\n      1      26\n      2c      28\n      2f      28\n      31      28\n      34      29\n      37      31\n      3a      31\n      3c      31\n      3f      32\n      42      35\n      45      35\n      47      35\n      4a      38\n   }\n\n   method 'testAssignmentType (Ljava/util/List;)V' {\n      1      41\n      3      42\n      f      43\n      10      43\n      12      43\n      18      46\n      1c      46\n      21      46\n      24      47\n   }\n}\n\nLines mapping:\n25 <-> 8\n26 <-> 9\n28 <-> 10\n29 <-> 11\n31 <-> 12\n32 <-> 13\n35 <-> 16\n36 <-> 17\n38 <-> 20\n41 <-> 23\n46 <-> 27\n48 <-> 29\n49 <-> 30\n51 <-> 32\n52 <-> 33\n55 <-> 36\n57 <-> 39\n61 <-> 42\n63 <-> 43\n64 <-> 44\n67 <-> 47\n68 <-> 48\n"
  },
  {
    "path": "testData/results/TestClassVar.dec",
    "content": "package pkg;\n\npublic class TestClassVar {\n   private boolean field_boolean = Math.random() > 0.0;\n   public int field_int = 0;\n\n   public void testFieldSSAU() {\n      for(int var1 = 0; var1 < 10; ++var1) {// 26\n         try {\n            System.out.println();// 29\n         } finally {\n            if (this.field_boolean) {// 32\n               System.out.println();// 33\n            }\n\n         }\n      }\n\n   }// 37\n\n   public Long testFieldSSAU1() {\n      return new Long((long)(this.field_int++));// 40\n   }\n\n   public void testComplexPropagation() {\n      int var1 = 0;// 45\n\n      while(var1 < 10) {// 47\n         int var2;\n         for(var2 = var1; var1 < 10 && var1 == 0; ++var1) {// 49 51\n         }\n\n         if (var2 != var1) {// 54\n            System.out.println();// 55\n         }\n      }\n\n   }// 58\n}\n\nclass 'pkg/TestClassVar' {\n   method 'testFieldSSAU ()V' {\n      0      7\n      1      7\n      3      7\n      5      7\n      8      9\n      b      9\n      1f      11\n      20      11\n      26      12\n      29      12\n      2e      7\n      34      18\n   }\n\n   method 'testFieldSSAU1 ()Ljava/lang/Long;' {\n      6      21\n      b      21\n      f      21\n      13      21\n   }\n\n   method 'testComplexPropagation ()V' {\n      0      25\n      1      25\n      3      27\n      5      27\n      9      29\n      b      29\n      d      29\n      11      29\n      14      29\n      1c      32\n      1f      33\n      22      33\n      28      37\n   }\n}\n\nLines mapping:\n26 <-> 8\n29 <-> 10\n32 <-> 12\n33 <-> 13\n37 <-> 19\n40 <-> 22\n45 <-> 26\n47 <-> 28\n49 <-> 30\n51 <-> 30\n54 <-> 33\n55 <-> 34\n58 <-> 38\nNot mapped:\n57\n"
  },
  {
    "path": "testData/results/TestCodeConstructs.dec",
    "content": "package pkg;\n\nclass TestCodeConstructs {\n   private int count = 0;\n\n   void expressions() {\n      (new String()).hashCode();// 20\n   }// 21\n\n   Integer fieldIncrement() {\n      return new Integer(this.count++);// 25\n   }\n}\n\nclass 'pkg/TestCodeConstructs' {\n   method 'expressions ()V' {\n      7      6\n      b      7\n   }\n\n   method 'fieldIncrement ()Ljava/lang/Integer;' {\n      6      10\n      b      10\n      12      10\n   }\n}\n\nLines mapping:\n20 <-> 7\n21 <-> 8\n25 <-> 11\n"
  },
  {
    "path": "testData/results/TestComplexInstanceOfRecordPatternJavac.dec",
    "content": "package pkg;\n\npublic class TestComplexInstanceOfRecordPatternJavac {\n   public static void main(String[] args) {\n   }// 6\n\n   public static void instanceOfTest1(Object o) {\n      if (o instanceof R(R(Object s1))) {// 9\n         System.out.println(s1);// 10\n         System.out.println(s1);// 11\n      }\n\n      System.out.println(\"1\");// 13\n   }\n\n   public static void instanceOfTest2(Object o) {\n      if (o instanceof R(R(String s1))) {// 17\n         System.out.println(s1);// 18\n         System.out.println(s1);// 19\n      }\n\n      System.out.println(\"4\");// 21\n   }\n\n   public static void instanceOfTest3(Object o) {\n      if (o instanceof R2(String s1, Object var8)) {// 25\n         System.out.println(s1);// 26\n         System.out.println(s1);// 27\n      }\n\n      System.out.println(\"12\");// 29\n   }\n\n   public static void instanceOfTest3_2(Object o) {\n      if (o instanceof R2(Object var8, String s1)) {// 33\n         System.out.println(s1);// 34\n         System.out.println(s1);// 35\n      }\n\n      System.out.println(\"3\");// 37\n   }\n\n   public static void instanceOfTest4(Object o) {\n      if (o instanceof R2(String s1, R(String s))) {// 41\n         System.out.println(s1);// 42\n         System.out.println(s);// 43\n         if (o instanceof R2(String s2, R(String s3))) {// 45\n            System.out.println(s2);// 46\n            System.out.println(s3);// 47\n         }\n      }\n\n      System.out.println(\"1\");// 50\n   }\n\n   static record R(Object o) {\n      R(Object o) {\n         this.o = o;\n      }\n\n      public Object o() {\n         return this.o;// 53\n      }\n   }\n\n   static record R2(Object o1, Object o2) {\n      R2(Object o1, Object o2) {\n         this.o1 = o1;\n         this.o2 = o2;\n      }\n\n      public Object o1() {\n         return this.o1;\n      }\n\n      public Object o2() {\n         return this.o2;// 56\n      }\n   }\n}\n\nclass 'pkg/TestComplexInstanceOfRecordPatternJavac' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n   }\n\n   method 'instanceOfTest1 (Ljava/lang/Object;)V' {\n      1      7\n      4      7\n      29      8\n      2d      8\n      30      9\n      34      9\n      37      12\n      3a      12\n      3c      12\n      3f      13\n   }\n\n   method 'instanceOfTest2 (Ljava/lang/Object;)V' {\n      1      16\n      4      16\n      34      17\n      38      17\n      3b      18\n      3f      18\n      42      21\n      45      21\n      47      21\n      4a      22\n   }\n\n   method 'instanceOfTest3 (Ljava/lang/Object;)V' {\n      1      25\n      4      25\n      29      26\n      2d      26\n      30      27\n      34      27\n      37      30\n      3a      30\n      3c      30\n      3f      31\n   }\n\n   method 'instanceOfTest3_2 (Ljava/lang/Object;)V' {\n      1      34\n      4      34\n      29      35\n      2d      35\n      30      36\n      34      36\n      37      39\n      3a      39\n      3c      39\n      3f      40\n   }\n\n   method 'instanceOfTest4 (Ljava/lang/Object;)V' {\n      1      43\n      4      43\n      49      44\n      4d      44\n      50      45\n      55      45\n      59      46\n      5c      46\n      a7      47\n      ac      47\n      af      48\n      b4      48\n      b7      52\n      ba      52\n      bc      52\n      bf      53\n   }\n}\n\nclass 'pkg/TestComplexInstanceOfRecordPatternJavac$R' {\n   method '<init> (Ljava/lang/Object;)V' {\n      6      57\n      9      58\n   }\n\n   method 'o ()Ljava/lang/Object;' {\n      1      61\n      4      61\n   }\n}\n\nclass 'pkg/TestComplexInstanceOfRecordPatternJavac$R2' {\n   method '<init> (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      6      67\n      b      68\n      e      69\n   }\n\n   method 'o1 ()Ljava/lang/Object;' {\n      1      72\n      4      72\n   }\n\n   method 'o2 ()Ljava/lang/Object;' {\n      1      76\n      4      76\n   }\n}\n\nLines mapping:\n6 <-> 5\n9 <-> 8\n10 <-> 9\n11 <-> 10\n13 <-> 13\n17 <-> 17\n18 <-> 18\n19 <-> 19\n21 <-> 22\n25 <-> 26\n26 <-> 27\n27 <-> 28\n29 <-> 31\n33 <-> 35\n34 <-> 36\n35 <-> 37\n37 <-> 40\n41 <-> 44\n42 <-> 45\n43 <-> 46\n45 <-> 47\n46 <-> 48\n47 <-> 49\n50 <-> 53\n53 <-> 62\n56 <-> 77\nNot mapped:\n14\n22\n30\n38\n51\n"
  },
  {
    "path": "testData/results/TestConstType.dec",
    "content": "package pkg;\n\npublic class TestConstType {\n   private char lineBreak = '\\n';\n   private char zero = 0;\n\n   public void setLineBreak(char os) {\n      switch (os) {// 8\n         case 'u':\n            this.lineBreak = '\\r';// 10\n            break;// 11\n         case 'w':\n            this.lineBreak = '\\n';// 14\n      }\n\n   }// 17\n\n   public void init() {\n      this.setLineBreak('w');// 20\n   }// 21\n\n   public String convertIndentation(String text) {\n      if (text.charAt(0) == '\\t') {// 24\n         text = text.replace('\\t', ' ');// 25\n      }\n\n      return text;// 27\n   }\n\n   public void printalot() {\n      System.out.println('a');// 31\n      System.out.println('\\t');// 32\n      System.out.println(0);// 34\n      System.out.println(65);// 35\n      System.out.println(120);// 36\n      System.out.println(32760);// 37\n      System.out.println(32761);// 38\n      System.out.println(35000);// 39\n      System.out.println(50000);// 40\n      System.out.println(128000);// 41\n      System.out.println(60793);// 42\n      System.out.println(60737);// 43\n      System.out.println(60777);// 44\n      System.out.println(60785);// 45\n      System.out.println(60835);// 46\n      System.out.println(60843);// 47\n      System.out.println(60851);// 48\n      System.out.println(60859);// 49\n      System.out.println(1048576);// 50\n      System.out.println(49152);// 51\n      System.out.println(44100);// 52\n      System.out.println(44101);// 53\n      System.out.println(44102);// 54\n      System.out.println(44103);// 55\n      System.out.println(60000);// 56\n      System.out.println(64000);// 57\n      System.out.println(65000);// 58\n      System.out.println(45000);// 59\n   }// 60\n\n   public char guessType(int val) {\n      if (0 <= val && val <= 127) {// 63\n         return 'X';// 64\n      } else if (-128 <= val && val <= 127) {// 66\n         return 'B';// 67\n      } else if (128 <= val && val <= 32767) {// 69\n         return 'Y';// 70\n      } else if (-32768 <= val && val <= 32767) {// 72\n         return 'S';// 73\n      } else {\n         return (char)(32768 <= val && val <= 65535 ? 'C' : 'I');// 75 76 79\n      }\n   }\n\n   public int getTypeMaxValue(char type) {\n      int maxValue;\n      switch (type) {// 85\n         case 'B':\n            maxValue = 127;// 90\n            break;// 91\n         case 'C':\n            maxValue = 65535;// 99\n            break;// 100\n         case 'S':\n            maxValue = 32767;// 96\n            break;// 97\n         case 'X':\n            maxValue = 128;// 87\n            break;// 88\n         case 'Y':\n            maxValue = 32768;// 93\n            break;// 94\n         default:\n            maxValue = 2147483647;// 102\n      }\n\n      return maxValue;// 104\n   }\n}\n\nclass 'pkg/TestConstType' {\n   method 'setLineBreak (C)V' {\n      1      7\n      1d      9\n      1f      9\n      22      10\n      26      12\n      28      12\n      2b      15\n   }\n\n   method 'init ()V' {\n      1      18\n      3      18\n      6      19\n   }\n\n   method 'convertIndentation (Ljava/lang/String;)Ljava/lang/String;' {\n      1      22\n      2      22\n      5      22\n      7      22\n      b      23\n      d      23\n      f      23\n      12      23\n      14      26\n   }\n\n   method 'printalot ()V' {\n      0      30\n      3      30\n      5      30\n      8      31\n      b      31\n      d      31\n      10      32\n      13      32\n      14      32\n      17      33\n      1a      33\n      1c      33\n      1f      34\n      22      34\n      24      34\n      27      35\n      2a      35\n      2d      35\n      30      36\n      33      36\n      36      36\n      39      37\n      3c      37\n      3e      37\n      41      38\n      44      38\n      46      38\n      49      39\n      4c      39\n      4e      39\n      51      40\n      54      40\n      56      40\n      59      41\n      5c      41\n      5e      41\n      61      42\n      64      42\n      66      42\n      69      43\n      6c      43\n      6e      43\n      71      44\n      74      44\n      76      44\n      79      45\n      7c      45\n      7e      45\n      81      46\n      84      46\n      86      46\n      89      47\n      8c      47\n      8e      47\n      91      48\n      94      48\n      96      48\n      99      49\n      9c      49\n      9e      49\n      a1      50\n      a4      50\n      a6      50\n      a9      51\n      ac      51\n      ae      51\n      b1      52\n      b4      52\n      b6      52\n      b9      53\n      bc      53\n      be      53\n      c1      54\n      c4      54\n      c6      54\n      c9      55\n      cc      55\n      ce      55\n      d1      56\n      d4      56\n      d6      56\n      d9      57\n      dc      57\n      de      57\n      e1      58\n   }\n\n   method 'guessType (I)C' {\n      0      61\n      2      61\n      6      61\n      8      61\n      b      62\n      d      62\n      e      63\n      11      63\n      15      63\n      17      63\n      1a      64\n      1c      64\n      1d      65\n      21      65\n      25      65\n      28      65\n      2b      66\n      2d      66\n      2e      67\n      32      67\n      36      67\n      39      67\n      3c      68\n      3e      68\n      3f      70\n      42      70\n      46      70\n      48      70\n      4b      70\n      4e      70\n   }\n\n   method 'getTypeMaxValue (C)I' {\n      1      76\n      34      87\n      37      87\n      38      88\n      3b      78\n      3d      78\n      3e      79\n      41      90\n      43      90\n      44      91\n      47      84\n      4a      84\n      4b      85\n      4e      81\n      50      81\n      51      82\n      54      93\n      56      93\n      58      96\n   }\n}\n\nLines mapping:\n8 <-> 8\n10 <-> 10\n11 <-> 11\n14 <-> 13\n17 <-> 16\n20 <-> 19\n21 <-> 20\n24 <-> 23\n25 <-> 24\n27 <-> 27\n31 <-> 31\n32 <-> 32\n34 <-> 33\n35 <-> 34\n36 <-> 35\n37 <-> 36\n38 <-> 37\n39 <-> 38\n40 <-> 39\n41 <-> 40\n42 <-> 41\n43 <-> 42\n44 <-> 43\n45 <-> 44\n46 <-> 45\n47 <-> 46\n48 <-> 47\n49 <-> 48\n50 <-> 49\n51 <-> 50\n52 <-> 51\n53 <-> 52\n54 <-> 53\n55 <-> 54\n56 <-> 55\n57 <-> 56\n58 <-> 57\n59 <-> 58\n60 <-> 59\n63 <-> 62\n64 <-> 63\n66 <-> 64\n67 <-> 65\n69 <-> 66\n70 <-> 67\n72 <-> 68\n73 <-> 69\n75 <-> 71\n76 <-> 71\n79 <-> 71\n85 <-> 77\n87 <-> 88\n88 <-> 89\n90 <-> 79\n91 <-> 80\n93 <-> 91\n94 <-> 92\n96 <-> 85\n97 <-> 86\n99 <-> 82\n100 <-> 83\n102 <-> 94\n104 <-> 97\n"
  },
  {
    "path": "testData/results/TestConstants.dec",
    "content": "package pkg;\n\npublic class TestConstants {\n   static final boolean T = true;\n   static final boolean F = false;\n   static final char C0 = '\\n';\n   static final char C1 = 'a';\n   static final char C2 = 'Ȁ';\n   static final byte BMin = -128;\n   static final byte BMax = 127;\n   static final short SMin = Short.MIN_VALUE;\n   static final short SMax = Short.MAX_VALUE;\n   static final int IMin = Integer.MIN_VALUE;\n   static final int IMax = Integer.MAX_VALUE;\n   static final long LMin = Long.MIN_VALUE;\n   static final long LMax = Long.MAX_VALUE;\n   static final float FNan = Float.NaN;\n   static final float FNeg = Float.NEGATIVE_INFINITY;\n   static final float FPos = Float.POSITIVE_INFINITY;\n   static final float FMin = Float.MIN_VALUE;\n   static final float FMax = Float.MAX_VALUE;\n   static final float FMinNormal = Float.MIN_NORMAL;\n   static final double DNan = Double.NaN;\n   static final double DNeg = Double.NEGATIVE_INFINITY;\n   static final double DPos = Double.POSITIVE_INFINITY;\n   static final double DMin = Double.MIN_VALUE;\n   static final double DMax = Double.MAX_VALUE;\n   static final double FDoubleNormal = Double.MIN_NORMAL;\n   static final double PI = Math.PI;\n   static final double E = Math.E;\n}\n\n"
  },
  {
    "path": "testData/results/TestConstantsAsIs.dec",
    "content": "package pkg;\n\nimport java.util.Date;\n\npublic class TestConstantsAsIs {\n   static final boolean T = true;\n   static final boolean F = false;\n   static final char C0 = '\\n';\n   static final char C1 = 'a';\n   static final char C2 = 'Ȁ';\n   static final byte BMin = -128;\n   static final byte BMax = 127;\n   static final short SMin = -32768;\n   static final short SMax = 32767;\n   static final int IMin = -2147483648;\n   static final int IMax = 2147483647;\n   static final long LMin = -9223372036854775808L;\n   static final long LMax = 9223372036854775807L;\n   static final float FNan = 0.0F / 0.0F;\n   static final float FNeg = -1.0F / 0.0F;\n   static final float FPos = 1.0F / 0.0F;\n   static final float FMin = 1.4E-45F;\n   static final float FMax = 3.4028235E38F;\n   static final double DNan = 0.0 / 0.0;\n   static final double DNeg = -1.0 / 0.0;\n   static final double DPos = 1.0 / 0.0;\n   static final double DMin = 4.9E-324;\n   static final double DMax = 1.7976931348623157E308;\n\n   @TestConstantsAsIs.A(byte.class)\n   void m1() {\n   }// 39\n\n   @TestConstantsAsIs.A(char.class)\n   void m2() {\n   }// 40\n\n   @TestConstantsAsIs.A(double.class)\n   void m3() {\n   }// 41\n\n   @TestConstantsAsIs.A(float.class)\n   void m4() {\n   }// 42\n\n   @TestConstantsAsIs.A(int.class)\n   void m5() {\n   }// 43\n\n   @TestConstantsAsIs.A(long.class)\n   void m6() {\n   }// 44\n\n   @TestConstantsAsIs.A(short.class)\n   void m7() {\n   }// 45\n\n   @TestConstantsAsIs.A(boolean.class)\n   void m8() {\n   }// 46\n\n   @TestConstantsAsIs.A(void.class)\n   void m9() {\n   }// 47\n\n   @TestConstantsAsIs.A(Date.class)\n   void m10() {\n   }// 48\n\n   @interface A {\n      Class<?> value();\n   }\n}\n\nclass 'pkg/TestConstantsAsIs' {\n   method 'm1 ()V' {\n      0      31\n   }\n\n   method 'm2 ()V' {\n      0      35\n   }\n\n   method 'm3 ()V' {\n      0      39\n   }\n\n   method 'm4 ()V' {\n      0      43\n   }\n\n   method 'm5 ()V' {\n      0      47\n   }\n\n   method 'm6 ()V' {\n      0      51\n   }\n\n   method 'm7 ()V' {\n      0      55\n   }\n\n   method 'm8 ()V' {\n      0      59\n   }\n\n   method 'm9 ()V' {\n      0      63\n   }\n\n   method 'm10 ()V' {\n      0      67\n   }\n}\n\nLines mapping:\n39 <-> 32\n40 <-> 36\n41 <-> 40\n42 <-> 44\n43 <-> 48\n44 <-> 52\n45 <-> 56\n46 <-> 60\n47 <-> 64\n48 <-> 68\n"
  },
  {
    "path": "testData/results/TestConstructorReference.dec",
    "content": "public class TestConstructorReference {\n   void boo() {\n      Runnable aNew = TestConstructorReference::new;// 22\n   }// 23\n}\n\nclass 'TestConstructorReference' {\n   method 'boo ()V' {\n      5      2\n      6      3\n   }\n}\n\nLines mapping:\n22 <-> 3\n23 <-> 4\n"
  },
  {
    "path": "testData/results/TestDebugSymbols.dec",
    "content": "package pkg;\n\nclass TestDebugSymbols {\n   private int m() {\n      String text = \"text\";// 6\n      long prolonged = 42L;// 7\n      float decimated = (float)prolonged / 10.0F;// 8\n      double doubled = (double)(2.0F * decimated);// 9\n      return (text + \":\" + prolonged + \":\" + decimated + \":\" + doubled).length();// 10\n   }\n\n   public void test() {\n      int i = 0;// 14\n      int count = 0;// 15\n\n      do {\n         i += count++;// 17\n      } while(i < 10);// 18\n\n   }// 19\n}\n\nclass 'pkg/TestDebugSymbols' {\n   method 'm ()I' {\n      0      4\n      2      4\n      3      5\n      6      5\n      8      6\n      9      6\n      b      6\n      c      6\n      e      7\n      11      7\n      12      7\n      13      7\n      20      8\n      29      8\n      33      8\n      3d      8\n      40      8\n      43      8\n   }\n\n   method 'test ()V' {\n      0      12\n      1      12\n      2      13\n      3      13\n      6      16\n      a      16\n      c      17\n      e      17\n      11      19\n   }\n}\n\nLines mapping:\n6 <-> 5\n7 <-> 6\n8 <-> 7\n9 <-> 8\n10 <-> 9\n14 <-> 13\n15 <-> 14\n17 <-> 17\n18 <-> 18\n19 <-> 20\n"
  },
  {
    "path": "testData/results/TestDeprecations.dec",
    "content": "package pkg;\n\npublic abstract class TestDeprecations {\n   /** @deprecated */\n   public int byComment;\n   /** @deprecated */\n   @Deprecated\n   public int byAnno;\n\n   /** @deprecated */\n   public void byComment() {\n      boolean var1 = true;// 27\n   }// 28\n\n   /** @deprecated */\n   public abstract void byCommentAbstract();\n\n   /** @deprecated */\n   @Deprecated\n   public void byAnno() {\n      boolean var1 = true;// 35\n   }// 36\n\n   /** @deprecated */\n   @Deprecated\n   public abstract void byAnnoAbstract();\n\n   /** @deprecated */\n   @Deprecated\n   public static class ByAnno {\n      int a = 5;\n\n      void foo() {\n         boolean var1 = true;// 55\n      }// 56\n   }\n\n   /** @deprecated */\n   public static class ByComment {\n      int a = 5;\n\n      void foo() {\n         boolean var1 = true;// 46\n      }// 47\n   }\n}\n\nclass 'pkg/TestDeprecations' {\n   method 'byComment ()V' {\n      0      11\n      1      11\n      2      12\n   }\n\n   method 'byAnno ()V' {\n      0      20\n      1      20\n      2      21\n   }\n}\n\nclass 'pkg/TestDeprecations$ByAnno' {\n   method 'foo ()V' {\n      0      33\n      1      33\n      2      34\n   }\n}\n\nclass 'pkg/TestDeprecations$ByComment' {\n   method 'foo ()V' {\n      0      42\n      1      42\n      2      43\n   }\n}\n\nLines mapping:\n27 <-> 12\n28 <-> 13\n35 <-> 21\n36 <-> 22\n46 <-> 43\n47 <-> 44\n55 <-> 34\n56 <-> 35\n"
  },
  {
    "path": "testData/results/TestDynamicConstantPoolEntry.dec",
    "content": "package java11;\n\npublic class TestDynamicConstantPoolEntry {\n   public static void main(String... var0) {\n      System.out.println(\"This class file contains constant pool entry of type CONSTANT_Dynamic, not used in the bytecode.\");\n   }\n}\n\nclass 'java11/TestDynamicConstantPoolEntry' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n      3      4\n      5      4\n      8      5\n   }\n}\n\nLines mapping:\n"
  },
  {
    "path": "testData/results/TestEmptyBlocks.dec",
    "content": "import java.util.Random;\n\npublic class TestEmptyBlocks {\n   public static void foo() {\n      try {\n         boolean var0 = false;// 22\n      } catch (Exception var1) {// 23\n      }\n\n      for(int i = 0; i < 5; ++i) {// 27\n      }\n\n      while((new Random()).nextBoolean()) {// 31\n      }\n\n      if ((new Random()).nextBoolean()) {// 35\n      }\n\n   }// 38\n}\n\nclass 'TestEmptyBlocks' {\n   method 'foo ()V' {\n      0      5\n      1      5\n      5      6\n      6      9\n      7      9\n      9      9\n      a      9\n      d      9\n      1a      12\n      2a      15\n      2d      15\n      30      18\n   }\n}\n\nLines mapping:\n22 <-> 6\n23 <-> 7\n27 <-> 10\n31 <-> 13\n35 <-> 16\n38 <-> 19\nNot mapped:\n25\n"
  },
  {
    "path": "testData/results/TestEnum.dec",
    "content": "package pkg;\n\npublic enum TestEnum {\n   E1,\n   E2 {\n      public void m() {\n      }// 22\n   },\n   E3(\"-\", TestEnum.Type.ODD),\n   E4(\"+\", TestEnum.Type.EVEN) {\n      public void m() {\n      }// 27\n   };\n\n   private String s;\n\n   public void m() {\n   }// 32\n\n   private TestEnum() {\n      this(\"?\", (Type)null);// 36\n   }\n\n   private TestEnum(@Deprecated String var3, Type var4) {\n      this.s = var3;// 37\n   }\n\n   private static enum Type {\n      ODD,\n      EVEN;\n   }\n}\n\nclass 'pkg/TestEnum$1' {\n   method 'm ()V' {\n      0      6\n   }\n}\n\nclass 'pkg/TestEnum$2' {\n   method 'm ()V' {\n      0      11\n   }\n}\n\nclass 'pkg/TestEnum' {\n   method 'm ()V' {\n      0      17\n   }\n\n   method '<init> (Ljava/lang/String;I)V' {\n      3      20\n      5      20\n      6      20\n      9      21\n   }\n\n   method '<init> (Ljava/lang/String;ILjava/lang/String;Lpkg/TestEnum$Type;)V' {\n      8      24\n      b      25\n   }\n}\n\nLines mapping:\n22 <-> 7\n27 <-> 12\n32 <-> 18\n36 <-> 21\n37 <-> 25\n"
  },
  {
    "path": "testData/results/TestExtendingSubclass.dec",
    "content": "package pkg;\n\npublic class TestExtendingSubclass {\n   class Subclass2 extends Subclass1 {\n      Subclass2(String name) {\n         super(name);// 14\n      }// 15\n   }\n\n   class Subclass1 {\n      Subclass1(String name) {\n      }// 9\n   }\n}\n\nclass 'pkg/TestExtendingSubclass$Subclass2' {\n   method '<init> (Lpkg/TestExtendingSubclass;Ljava/lang/String;)V' {\n      8      5\n      b      6\n   }\n}\n\nclass 'pkg/TestExtendingSubclass$Subclass1' {\n   method '<init> (Lpkg/TestExtendingSubclass;Ljava/lang/String;)V' {\n      9      11\n   }\n}\n\nLines mapping:\n9 <-> 12\n14 <-> 6\n15 <-> 7\nNot mapped:\n8\n13\n"
  },
  {
    "path": "testData/results/TestExtendsList.dec",
    "content": "package pkg;\n\npublic class TestExtendsList {\n   static <T extends Comparable<? super T>> T m1(T var0) {\n      return null;// 20\n   }\n\n   static <T extends Object & Comparable<? super T>> T m2(T var0) {\n      return null;// 24\n   }\n}\n\nclass 'pkg/TestExtendsList' {\n   method 'm1 (Ljava/lang/Comparable;)Ljava/lang/Comparable;' {\n      0      4\n      1      4\n   }\n\n   method 'm2 (Ljava/lang/Object;)Ljava/lang/Object;' {\n      0      8\n      1      8\n   }\n}\n\nLines mapping:\n20 <-> 5\n24 <-> 9\n"
  },
  {
    "path": "testData/results/TestFieldSingleAccess.dec",
    "content": "package pkg;\n\npublic final class TestFieldSingleAccess {\n   public Integer field;\n\n   public final void test() {\n      Integer var10000 = this.field;\n      if (var10000 != null) {\n         System.out.println(var10000);\n      }\n\n   }\n\n   public final void test1() {\n      synchronized(this.field) {\n         System.out.println('1');\n      }\n   }\n}\n\nclass 'pkg/TestFieldSingleAccess' {\n   method 'test ()V' {\n      1      6\n      5      7\n      8      8\n      c      8\n      f      11\n   }\n\n   method 'test1 ()V' {\n      1      14\n      6      14\n      7      15\n      a      15\n      c      15\n      19      17\n   }\n}\n\nLines mapping:\n"
  },
  {
    "path": "testData/results/TestGenericArgs.dec",
    "content": "package pkg;\n\npublic class TestGenericArgs {\n   private static <T> void genericSingle(Class<T> var0) {\n   }// 4\n\n   private static <T> void genericVarArgs(Class<T>... var0) {\n   }// 6\n\n   private static <T> void genericArray(Class<T>[] var0) {\n   }// 8\n\n   private static <T> void single(Class var0) {\n   }// 10\n\n   private static <T> void varArgs(Class... var0) {\n   }// 12\n\n   private static <T> void array(Class[] var0) {\n   }// 14\n}\n\nclass 'pkg/TestGenericArgs' {\n   method 'genericSingle (Ljava/lang/Class;)V' {\n      0      4\n   }\n\n   method 'genericVarArgs ([Ljava/lang/Class;)V' {\n      0      7\n   }\n\n   method 'genericArray ([Ljava/lang/Class;)V' {\n      0      10\n   }\n\n   method 'single (Ljava/lang/Class;)V' {\n      0      13\n   }\n\n   method 'varArgs ([Ljava/lang/Class;)V' {\n      0      16\n   }\n\n   method 'array ([Ljava/lang/Class;)V' {\n      0      19\n   }\n}\n\nLines mapping:\n4 <-> 5\n6 <-> 8\n8 <-> 11\n10 <-> 14\n12 <-> 17\n14 <-> 20\n"
  },
  {
    "path": "testData/results/TestGroovyClass.dec",
    "content": "package pkg;\n\nimport groovy.lang.Closure;\nimport groovy.lang.GroovyObject;\nimport groovy.lang.MetaClass;\nimport java.util.concurrent.Callable;\nimport org.codehaus.groovy.runtime.GeneratedClosure;\nimport org.codehaus.groovy.runtime.ScriptBytecodeAdapter;\nimport org.codehaus.groovy.runtime.callsite.CallSite;\n\npublic class TestGroovyClass implements GroovyObject {\n   private final Nested n;\n   private final Inner i;\n   private final Runnable r;\n   private final Callable<String> c;\n\n   public TestGroovyClass() {\n      CallSite[] var1 = $getCallSiteArray();\n      Object var2 = var1[0].callConstructor(Nested.class);// 9\n      this.n = (Nested)ScriptBytecodeAdapter.castToType(var2, Nested.class);\n      Object var3 = var1[1].callConstructor(Inner.class, this);\n      this.i = (Inner)ScriptBytecodeAdapter.castToType(var3, Inner.class);\n      _closure1 var4 = new _closure1(this, this);// 10\n      this.r = var4;\n      _closure2 var5 = new _closure2(this, this);\n      this.c = var5;\n      MetaClass var6 = this.$getStaticMetaClass();\n      this.metaClass = var6;\n   }\n\n   public final Nested getN() {\n      return this.n;\n   }\n\n   public final Inner getI() {\n      return this.i;\n   }\n\n   public final Runnable getR() {\n      return this.r;\n   }\n\n   public final Callable<String> getC() {\n      return this.c;\n   }\n\n   public static class Nested implements GroovyObject {\n      public Nested() {\n         CallSite[] var1 = $getCallSiteArray();\n         MetaClass var2 = this.$getStaticMetaClass();\n         this.metaClass = var2;\n      }\n   }\n\n   public class Inner implements GroovyObject {\n      public Inner() {\n         CallSite[] var2 = $getCallSiteArray();\n         super();\n         MetaClass var4 = this.$getStaticMetaClass();\n         this.metaClass = var4;\n      }\n   }\n\n   public class _closure1 extends Closure implements GeneratedClosure {\n      public _closure1(Object _outerInstance, Object _thisObject) {\n         CallSite[] var3 = $getCallSiteArray();\n         super(_outerInstance, _thisObject);\n      }\n\n      public Object doCall(Object it) {\n         CallSite[] var2 = $getCallSiteArray();\n         return var2[0].callCurrent(this, \"I'm runnable\");// 11\n      }\n\n      public Object doCall() {\n         CallSite[] var1 = $getCallSiteArray();\n         return this.doCall((Object)null);\n      }\n   }\n\n   public class _closure2 extends Closure implements GeneratedClosure {\n      public _closure2(Object _outerInstance, Object _thisObject) {\n         CallSite[] var3 = $getCallSiteArray();\n         super(_outerInstance, _thisObject);\n      }\n\n      public Object doCall(Object it) {\n         CallSite[] var2 = $getCallSiteArray();\n         return \"I'm callable\";// 12\n      }\n\n      public Object doCall() {\n         CallSite[] var1 = $getCallSiteArray();\n         return this.doCall((Object)null);\n      }\n   }\n}\n\nclass 'pkg/TestGroovyClass' {\n   method '<init> ()V' {\n      4      17\n      7      17\n      9      18\n      b      18\n      c      18\n      e      18\n      13      18\n      15      19\n      17      19\n      1a      19\n      1f      19\n      25      20\n      27      20\n      28      20\n      2b      20\n      30      20\n      32      21\n      34      21\n      37      21\n      3c      21\n      4a      22\n      50      23\n      5f      24\n      65      25\n      6c      26\n      6f      26\n      75      27\n      7b      28\n   }\n\n   method 'getN ()Lpkg/TestGroovyClass$Nested;' {\n      1      31\n      4      31\n   }\n\n   method 'getI ()Lpkg/TestGroovyClass$Inner;' {\n      1      35\n      4      35\n   }\n\n   method 'getR ()Ljava/lang/Runnable;' {\n      1      39\n      4      39\n   }\n\n   method 'getC ()Ljava/util/concurrent/Callable;' {\n      1      43\n      4      43\n   }\n}\n\nclass 'pkg/TestGroovyClass$Nested' {\n   method '<init> ()V' {\n      4      48\n      7      48\n      9      49\n      c      49\n      10      50\n      15      51\n   }\n}\n\nclass 'pkg/TestGroovyClass$Inner' {\n   method '<init> (Lpkg/TestGroovyClass;)V' {\n      0      56\n      3      56\n      f      57\n      13      58\n      16      58\n      1c      59\n      22      60\n   }\n}\n\nclass 'pkg/TestGroovyClass$_closure1' {\n   method '<init> (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      0      65\n      3      65\n      7      66\n      a      67\n   }\n\n   method 'doCall (Ljava/lang/Object;)Ljava/lang/Object;' {\n      0      70\n      3      70\n      5      71\n      7      71\n      9      71\n      b      71\n      10      71\n   }\n\n   method 'doCall ()Ljava/lang/Object;' {\n      0      75\n      3      75\n      5      76\n      6      76\n      9      76\n   }\n}\n\nclass 'pkg/TestGroovyClass$_closure2' {\n   method '<init> (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      0      82\n      3      82\n      7      83\n      a      84\n   }\n\n   method 'doCall (Ljava/lang/Object;)Ljava/lang/Object;' {\n      0      87\n      3      87\n      4      88\n      6      88\n   }\n\n   method 'doCall ()Ljava/lang/Object;' {\n      0      92\n      3      92\n      5      93\n      6      93\n      9      93\n   }\n}\n\nLines mapping:\n9 <-> 19\n10 <-> 23\n11 <-> 72\n12 <-> 89\n"
  },
  {
    "path": "testData/results/TestGroovyTrait.dec",
    "content": "package pkg;\n\nimport groovy.transform.Trait;\nimport org.codehaus.groovy.transform.trait.Traits.Implemented;\n\n@Trait\npublic interface TestGroovyTrait {\n   @Implemented\n   Object myMethod();\n\n   @Implemented\n   Object getMyField();\n\n   @Implemented\n   void setMyField(Object var1);\n}\n\n"
  },
  {
    "path": "testData/results/TestIffSimplification.dec",
    "content": "package pkg;\n\npublic class TestIffSimplification {\n   public int simpleIff(boolean status, int[] values) {\n      return status ? values[0] : values[1];// 7\n   }\n\n   public int simpleIf(boolean status, int[] values) {\n      return status ? values[0] : values[1];// 11 12 15\n   }\n\n   public int nestedIf(boolean status, boolean condition, int[] values) {\n      if (status) {// 20\n         return condition ? values[2] : values[0];// 21 22 25\n      } else {\n         return values[1];// 29\n      }\n   }\n\n   public int compareTo(int mc1, int mc2, byte csg1, byte csg2, double score1, double score2, int doc1, int doc2) {\n      if (mc1 != mc2) {// 34\n         return mc1 < mc2 ? 1 : -1;// 35\n      } else if (csg1 != csg2) {// 38\n         return csg1 < csg2 ? 1 : -1;// 39\n      } else if (Math.abs(score1 - score2) < 1.0E-6) {// 42\n         return doc1 < doc2 ? -1 : 1;// 43\n      } else {\n         return score1 < score2 ? 1 : -1;// 46\n      }\n   }\n}\n\nclass 'pkg/TestIffSimplification' {\n   method 'simpleIff (Z[I)I' {\n      1      4\n      5      4\n      6      4\n      b      4\n      c      4\n      d      4\n   }\n\n   method 'simpleIf (Z[I)I' {\n      1      8\n      5      8\n      6      8\n      9      8\n      a      8\n   }\n\n   method 'nestedIf (ZZ[I)I' {\n      1      12\n      5      13\n      9      13\n      a      13\n      d      13\n      e      13\n      11      15\n      12      15\n      13      15\n   }\n\n   method 'compareTo (IIBBDDII)I' {\n      2      20\n      7      21\n      a      21\n      e      21\n      f      21\n      13      22\n      19      23\n      1c      23\n      20      23\n      21      23\n      26      24\n      27      24\n      2a      24\n      2d      24\n      2e      24\n      35      25\n      38      25\n      3c      25\n      3d      25\n      42      27\n      43      27\n      46      27\n      4a      27\n      4b      27\n   }\n}\n\nLines mapping:\n7 <-> 5\n11 <-> 9\n12 <-> 9\n15 <-> 9\n20 <-> 13\n21 <-> 14\n22 <-> 14\n25 <-> 14\n29 <-> 16\n34 <-> 21\n35 <-> 22\n38 <-> 23\n39 <-> 24\n42 <-> 25\n43 <-> 26\n46 <-> 28\n"
  },
  {
    "path": "testData/results/TestIllegalVarName.dec",
    "content": "package pkg;\n\nimport kotlin.Metadata;\nimport kotlin.jvm.internal.Intrinsics;\nimport org.jetbrains.annotations.NotNull;\n\n@Metadata(\n   mv = {1, 1, 0},\n   bv = {1, 0, 0},\n   k = 1,\n   d1 = {\"\\u0000\\u001a\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0002\\n\\u0002\\u0010\\u000e\\n\\u0002\\b\\u0002\\n\\u0002\\u0010\\b\\n\\u0000\\u0018\\u00002\\u00020\\u0001B\\u0005¢\\u0006\\u0002\\u0010\\u0002J\\u0016\\u0010\\u0003\\u001a\\u00020\\u00042\\u0006\\u0010\\u0005\\u001a\\u00020\\u00042\\u0006\\u0010\\u0006\\u001a\\u00020\\u0007¨\\u0006\\b\"},\n   d2 = {\"Lpkg/TestIllegalVarName;\", \"\", \"()V\", \"m\", \"\", \"this\", \"enum\", \"\", \"java-decompiler-plugin\"}\n)\npublic final class TestIllegalVarName {\n   @NotNull\n   public final String m(@NotNull String var1, int var2) {\n      Intrinsics.checkParameterIsNotNull(var1, \"this\");\n      return var1 + '/' + var2;// 5\n   }\n}\n\nclass 'pkg/TestIllegalVarName' {\n   method 'm (Ljava/lang/String;I)Ljava/lang/String;' {\n      1      16\n      3      16\n      11      17\n      1a      17\n      1d      17\n   }\n}\n\nLines mapping:\n5 <-> 18\n"
  },
  {
    "path": "testData/results/TestInUse.dec",
    "content": "package pkg;\n\npublic class TestInUse {\n   public int getInt() {\n      return 42;// 5\n   }\n\n   protected int reuse() {\n      int i = 0;// 9\n      int d = 0;\n      int result = 0;// 10\n\n      do {\n         d = this.getInt();// 12\n         result -= d;// 13\n         ++i;// 15\n      } while(i < 10);\n\n      return result;// 16\n   }\n}\n\nclass 'pkg/TestInUse' {\n   method 'getInt ()I' {\n      0      4\n      2      4\n   }\n\n   method 'reuse ()I' {\n      0      8\n      1      8\n      2      9\n      3      9\n      4      10\n      5      10\n      7      13\n      a      13\n      e      14\n      f      15\n      13      16\n      15      16\n      19      18\n   }\n}\n\nLines mapping:\n5 <-> 5\n9 <-> 9\n10 <-> 11\n12 <-> 14\n13 <-> 15\n15 <-> 16\n16 <-> 19\n"
  },
  {
    "path": "testData/results/TestInheritanceChainCycle.dec",
    "content": "package pkg;\n\npublic class TestInheritanceChainCycle extends TestInheritanceChainCycle {\n   public void printMessage() {\n      System.out.println(\"Hello, bug!\");// 21 22 23\n   }// 24\n}\n\nclass 'pkg/TestInheritanceChainCycle' {\n   method 'printMessage ()V' {\n      0      4\n      3      4\n      5      4\n      8      5\n   }\n}\n\nLines mapping:\n21 <-> 5\n22 <-> 5\n23 <-> 5\n24 <-> 6\n"
  },
  {
    "path": "testData/results/TestInner2.dec",
    "content": "package pkg;\n\nclass TestInner2 {\n   private TestInner2() {\n   }// 4\n\n   private TestInner2(int a) {\n   }// 5\n\n   static class AnotherStatic2 extends TestInner2 {\n      AnotherStatic2() {\n         super(2);// 25\n      }// 26\n   }\n\n   class Another2 extends TestInner2 {\n      Another2() {\n         super(2);// 19\n      }// 20\n   }\n\n   static class AnotherStatic extends TestInner2 {\n      AnotherStatic() {\n      }// 14\n   }\n\n   class Another extends TestInner2 {\n      Another() {\n      }// 9\n   }\n}\n\nclass 'pkg/TestInner2' {\n   method '<init> ()V' {\n      4      4\n   }\n\n   method '<init> (I)V' {\n      4      7\n   }\n}\n\nclass 'pkg/TestInner2$AnotherStatic2' {\n   method '<init> ()V' {\n      1      11\n      2      11\n      3      11\n      6      12\n   }\n}\n\nclass 'pkg/TestInner2$Another2' {\n   method '<init> (Lpkg/TestInner2;)V' {\n      6      17\n      7      17\n      8      17\n      b      18\n   }\n}\n\nclass 'pkg/TestInner2$AnotherStatic' {\n   method '<init> ()V' {\n      1      23\n      2      23\n      5      24\n   }\n}\n\nclass 'pkg/TestInner2$Another' {\n   method '<init> (Lpkg/TestInner2;)V' {\n      6      29\n      7      29\n      a      30\n   }\n}\n\nLines mapping:\n4 <-> 5\n5 <-> 8\n8 <-> 30\n9 <-> 31\n13 <-> 24\n14 <-> 25\n19 <-> 18\n20 <-> 19\n25 <-> 12\n26 <-> 13\nNot mapped:\n18\n"
  },
  {
    "path": "testData/results/TestInnerClassConstructor.dec",
    "content": "package pkg;\n\nclass TestInnerClassConstructor {\n   void l() {\n      new Inner(\"text\");// 5\n   }// 6\n\n   void m() {\n      new Another(3, 4);// 9\n   }// 10\n\n   void n(String var1) {\n      System.out.println(\"n(): \" + var1);// 13\n   }// 14\n\n   private class Another {\n      private Another(int var2, int var3) {\n         TestInnerClassConstructor.this.n(var2 + \"+\" + var3);// 24\n      }// 25\n   }\n\n   final class Inner {\n      private Inner(String var2) {\n         TestInnerClassConstructor.this.n(var2);// 18\n      }// 19\n   }\n}\n\nclass 'pkg/TestInnerClassConstructor' {\n   method 'l ()V' {\n      5      4\n      c      5\n   }\n\n   method 'm ()V' {\n      5      8\n      6      8\n      c      9\n   }\n\n   method 'n (Ljava/lang/String;)V' {\n      0      12\n      a      12\n      13      12\n      16      12\n      19      13\n   }\n}\n\nclass 'pkg/TestInnerClassConstructor$Another' {\n   method '<init> (Lpkg/TestInnerClassConstructor;II)V' {\n      15      17\n      1e      17\n      21      17\n      24      18\n   }\n}\n\nclass 'pkg/TestInnerClassConstructor$Inner' {\n   method '<init> (Lpkg/TestInnerClassConstructor;Ljava/lang/String;)V' {\n      b      23\n      e      24\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 6\n9 <-> 9\n10 <-> 10\n13 <-> 13\n14 <-> 14\n18 <-> 24\n19 <-> 25\n24 <-> 18\n25 <-> 19\nNot mapped:\n17\n23\n"
  },
  {
    "path": "testData/results/TestInnerLocal.dec",
    "content": "package pkg;\n\npublic class TestInnerLocal {\n   public static void testStaticMethod() {\n      class Inner {\n         final String x;\n\n         public Inner(@Deprecated String x) {\n            this.x = x;// 8\n         }// 9\n      }\n\n      new Inner(\"test\");// 11\n      new Inner1Static(\"test\");// 12\n      new Inner1Static.Inner2Static(\"test\");// 13\n   }// 14\n\n   public void testMethod() {\n      class Inner {\n         final String x;\n\n         public Inner(@Deprecated String x) {\n            this.x = x;// 20\n         }// 21\n      }\n\n      new Inner(\"test\");// 23\n      new Inner1Static(\"test\");// 24\n      new Inner1(\"test\");// 25\n      new Inner1Static.Inner2Static(\"test\");// 26\n   }// 27\n\n   static class Inner1Static {\n      final String x;\n\n      public Inner1Static(@Deprecated String x) {\n         this.x = x;// 39\n      }// 40\n\n      public static class Inner2Static {\n         final String x;\n\n         public Inner2Static(@Deprecated String x) {\n            this.x = x;// 45\n         }// 46\n      }\n   }\n\n   class Inner1 {\n      final String x;\n\n      public Inner1(@Deprecated String x) {\n         this.x = x;// 32\n      }// 33\n   }\n}\n\nclass 'pkg/TestInnerLocal$1Inner' {\n   method '<init> (Ljava/lang/String;)V' {\n      6      8\n      9      9\n   }\n}\n\nclass 'pkg/TestInnerLocal' {\n   method 'testStaticMethod ()V' {\n      4      12\n      e      13\n      18      14\n      1e      15\n   }\n\n   method 'testMethod ()V' {\n      5      26\n      f      27\n      1a      28\n      24      29\n      2a      30\n   }\n}\n\nclass 'pkg/TestInnerLocal$2Inner' {\n   method '<init> (Lpkg/TestInnerLocal;Ljava/lang/String;)V' {\n      b      22\n      e      23\n   }\n}\n\nclass 'pkg/TestInnerLocal$Inner1Static' {\n   method '<init> (Ljava/lang/String;)V' {\n      6      36\n      9      37\n   }\n}\n\nclass 'pkg/TestInnerLocal$Inner1Static$Inner2Static' {\n   method '<init> (Ljava/lang/String;)V' {\n      6      43\n      9      44\n   }\n}\n\nclass 'pkg/TestInnerLocal$Inner1' {\n   method '<init> (Lpkg/TestInnerLocal;Ljava/lang/String;)V' {\n      b      52\n      e      53\n   }\n}\n\nLines mapping:\n8 <-> 9\n9 <-> 10\n11 <-> 13\n12 <-> 14\n13 <-> 15\n14 <-> 16\n20 <-> 23\n21 <-> 24\n23 <-> 27\n24 <-> 28\n25 <-> 29\n26 <-> 30\n27 <-> 31\n32 <-> 53\n33 <-> 54\n39 <-> 37\n40 <-> 38\n45 <-> 44\n46 <-> 45\nNot mapped:\n7\n19\n31\n38\n44\n"
  },
  {
    "path": "testData/results/TestInnerSignature.dec",
    "content": "package pkg;\n\npublic class TestInnerSignature<A, B, C> {\n   A a;\n   B b;\n   C c;\n\n   public TestInnerSignature(A a, @Deprecated B b, C c) {\n      this.a = a;// 9\n      this.b = b;// 10\n      this.c = c;// 11\n   }// 12\n\n   public static class InnerStatic<A, B, C> {\n      A a;\n      B b;\n      C c;\n\n      public InnerStatic(A a, @Deprecated B b, C c) {\n         this.a = a;// 32\n         this.b = b;// 33\n         this.c = c;// 34\n      }// 35\n   }\n\n   public class Inner {\n      A a;\n      B b;\n      C c;\n\n      public Inner(A a, @Deprecated B b, C c) {\n         this.a = a;// 20\n         this.b = b;// 21\n         this.c = c;// 22\n      }// 23\n   }\n}\n\nclass 'pkg/TestInnerSignature' {\n   method '<init> (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V' {\n      6      8\n      b      9\n      10      10\n      13      11\n   }\n}\n\nclass 'pkg/TestInnerSignature$InnerStatic' {\n   method '<init> (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V' {\n      6      19\n      b      20\n      10      21\n      13      22\n   }\n}\n\nclass 'pkg/TestInnerSignature$Inner' {\n   method '<init> (Lpkg/TestInnerSignature;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V' {\n      b      31\n      10      32\n      16      33\n      19      34\n   }\n}\n\nLines mapping:\n9 <-> 9\n10 <-> 10\n11 <-> 11\n12 <-> 12\n20 <-> 32\n21 <-> 33\n22 <-> 34\n23 <-> 35\n32 <-> 20\n33 <-> 21\n34 <-> 22\n35 <-> 23\nNot mapped:\n8\n19\n31\n"
  },
  {
    "path": "testData/results/TestInstanceofPatternNotSupported.dec",
    "content": "package decompiler;\n\npublic class TestInstanceofPatternNotSupported {\n   void typePattern(Object str) {\n      if (!(str instanceof String)) {// 5\n         System.out.println(\"no\");// 6\n      } else {\n         String s = (String)str;// 9\n         if (s.length() > 3) {// 10\n            System.out.println(s);// 11\n         } else if (s.startsWith(\"a\")) {// 12\n            System.out.println(\"\" + s);// 13\n         }\n\n      }\n   }// 7 15\n}\n\nclass 'decompiler/TestInstanceofPatternNotSupported' {\n   method 'typePattern (Ljava/lang/Object;)V' {\n      1      4\n      4      4\n      7      5\n      a      5\n      c      5\n      f      15\n      11      7\n      14      7\n      16      8\n      19      8\n      1a      8\n      1d      9\n      21      9\n      28      10\n      2a      10\n      2d      10\n      30      11\n      34      11\n      39      11\n      3c      15\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 6\n7 <-> 16\n9 <-> 8\n10 <-> 9\n11 <-> 10\n12 <-> 11\n13 <-> 12\n15 <-> 16\n"
  },
  {
    "path": "testData/results/TestInstanceofWithPattern.dec",
    "content": "package decompiler;\n\nimport java.util.Collection;\nimport java.util.List;\n\npublic class TestInstanceofWithPattern {\n   void typePattern1(Object str) {\n      if (str instanceof String s) {// 9\n         System.out.println(s);// 10\n      } else {\n         System.out.println(\"no\");// 12\n      }\n\n   }// 14\n\n   void typePattern2(Object str) {\n      if (!(str instanceof String s)) {// 17\n         System.out.println(\"no\");// 18\n      } else {\n         if (s.length() > 3) {// 22\n            System.out.println(s);// 23\n         } else if (s.startsWith(\"a\")) {// 24\n            System.out.println(\"\" + s);// 25\n         }\n\n      }\n   }// 19 27\n\n   void typePatternInBinaryExpr(Object str) {\n      if (str instanceof String s) {// 30\n         if (s.length() > 1 || s.startsWith(\"a\")) {\n            System.out.println(s);// 31\n            return;// 35\n         }\n      }\n\n      System.out.println(\"no\");// 33\n   }\n\n   String returnInstanceof(Object obj) {\n      if (obj instanceof String s) {// 38\n         if (s.length() > 50) {\n            return \"\\\"\" + s.substring(0, 50) + \"...\\\"\";// 39\n         }\n      }\n\n      if (obj instanceof String s) {// 41\n         return \"\\\"\" + s + \"\\\"\";// 42\n      } else if (obj instanceof Collection<?> c) {// 44\n         return \"Collection (size = \" + c.size() + \")\";// 45\n      } else {\n         return obj.toString();// 47\n      }\n   }\n\n   String complex(Object obj1, Object obj2) {\n      while(true) {\n         try {\n            if (obj1 instanceof String s) {// 53\n               while(!s.startsWith(\"a\")) {// 55\n               }\n\n               return s;// 56\n            }\n\n            if (obj2 instanceof Collection<?> c) {// 59\n               return c.toString();// 60\n            }\n         } catch (Exception var7) {// 62\n            if (obj2 instanceof String s) {// 63\n               while(!s.startsWith(\"b\")) {// 65\n               }\n\n               return s + \"b\";// 66\n            }\n\n            if (obj2 instanceof List<?> l) {// 69\n               String var10000 = this.getStr();// 70\n               return var10000 + l.size();\n            }\n         }\n      }\n   }\n\n   String getStr() {\n      return null;// 77\n   }\n}\n\nclass 'decompiler/TestInstanceofWithPattern' {\n   method 'typePattern1 (Ljava/lang/Object;)V' {\n      3      7\n      6      7\n      e      8\n      12      8\n      18      10\n      1b      10\n      1d      10\n      20      13\n   }\n\n   method 'typePattern2 (Ljava/lang/Object;)V' {\n      1      16\n      4      16\n      7      17\n      a      17\n      c      17\n      f      26\n      16      19\n      19      19\n      1a      19\n      1d      20\n      21      20\n      28      21\n      2a      21\n      2d      21\n      30      22\n      34      22\n      39      22\n      3c      26\n   }\n\n   method 'typePatternInBinaryExpr (Ljava/lang/Object;)V' {\n      3      29\n      6      29\n      f      30\n      12      30\n      13      30\n      17      30\n      19      30\n      1c      30\n      1f      31\n      23      31\n      29      36\n      2c      36\n      2e      36\n      31      32\n   }\n\n   method 'returnInstanceof (Ljava/lang/Object;)Ljava/lang/String;' {\n      3      40\n      6      40\n      f      41\n      12      41\n      14      41\n      18      42\n      19      42\n      1b      42\n      1e      42\n      23      42\n      27      46\n      2a      46\n      33      47\n      38      47\n      3c      48\n      3f      48\n      48      49\n      4d      49\n      52      49\n      54      51\n      57      51\n   }\n\n   method 'complex (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/String;' {\n      5      58\n      8      58\n      14      59\n      16      59\n      19      59\n      1e      62\n      24      65\n      27      65\n      31      66\n      34      66\n      38      68\n      3e      69\n      41      69\n      4d      70\n      4f      70\n      52      70\n      57      73\n      5c      73\n      62      76\n      65      76\n      70      77\n      75      78\n      7a      78\n      7f      78\n   }\n\n   method 'getStr ()Ljava/lang/String;' {\n      0      85\n      1      85\n   }\n}\n\nLines mapping:\n9 <-> 8\n10 <-> 9\n12 <-> 11\n14 <-> 14\n17 <-> 17\n18 <-> 18\n19 <-> 27\n22 <-> 20\n23 <-> 21\n24 <-> 22\n25 <-> 23\n27 <-> 27\n30 <-> 30\n31 <-> 32\n33 <-> 37\n35 <-> 33\n38 <-> 41\n39 <-> 43\n41 <-> 47\n42 <-> 48\n44 <-> 49\n45 <-> 50\n47 <-> 52\n53 <-> 59\n55 <-> 60\n56 <-> 63\n59 <-> 66\n60 <-> 67\n62 <-> 69\n63 <-> 70\n65 <-> 71\n66 <-> 74\n69 <-> 77\n70 <-> 78\n77 <-> 86\nNot mapped:\n21\n72\n"
  },
  {
    "path": "testData/results/TestIntVarMerge.dec",
    "content": "package pkg;\n\npublic class TestIntVarMerge {\n   public int test1() {\n      int var1 = 7;// 5\n      var1 = 23 * var1;// 6\n      var1 *= 23;// 7\n      return var1;// 8\n   }\n\n   public void test2() {\n      int var1 = 3;// 12\n      System.out.println(var1);// 13\n      ++var1;// 14\n      System.out.println(var1);// 15\n      ++var1;// 16\n      System.out.println(var1);// 17\n   }// 18\n}\n\nclass 'pkg/TestIntVarMerge' {\n   method 'test1 ()I' {\n      0      4\n      2      4\n      3      5\n      6      5\n      7      5\n      9      6\n      c      6\n      e      7\n   }\n\n   method 'test2 ()V' {\n      0      11\n      1      11\n      2      12\n      6      12\n      9      13\n      c      14\n      10      14\n      13      15\n      16      16\n      1a      16\n      1d      17\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 6\n7 <-> 7\n8 <-> 8\n12 <-> 12\n13 <-> 13\n14 <-> 14\n15 <-> 15\n16 <-> 16\n17 <-> 17\n18 <-> 18\n"
  },
  {
    "path": "testData/results/TestInterfaceFields.dec",
    "content": "package pkg;\n\nimport java.math.BigDecimal;\n\npublic interface TestInterfaceFields {\n   BigDecimal BIG_ZERO = BigDecimal.ZERO;\n   int MAX_BYTE_VALUE = 127;\n}\n\n"
  },
  {
    "path": "testData/results/TestInterfaceMethods.dec",
    "content": "package pkg;\n\npublic interface TestInterfaceMethods {\n   static void staticMethod() {\n   }// 4\n\n   default void defaultMethod() {\n   }// 5\n}\n\nclass 'pkg/TestInterfaceMethods' {\n   method 'staticMethod ()V' {\n      0      4\n   }\n\n   method 'defaultMethod ()V' {\n      0      7\n   }\n}\n\nLines mapping:\n4 <-> 5\n5 <-> 8\n"
  },
  {
    "path": "testData/results/TestInterfaceSuper.dec",
    "content": "package pkg;\n\npublic interface TestInterfaceSuper {\n   default void defaultMethod() {\n   }// 4\n\n   public static class Impl implements TestInterfaceSuper {\n      public void defaultMethod() {\n         TestInterfaceSuper.super.defaultMethod();// 8\n      }// 9\n   }\n}\n\nclass 'pkg/TestInterfaceSuper' {\n   method 'defaultMethod ()V' {\n      0      4\n   }\n}\n\nclass 'pkg/TestInterfaceSuper$Impl' {\n   method 'defaultMethod ()V' {\n      1      8\n      4      9\n   }\n}\n\nLines mapping:\n4 <-> 5\n8 <-> 9\n9 <-> 10\n"
  },
  {
    "path": "testData/results/TestInvertedFloatComparison.dec",
    "content": "package pkg;\n\npublic class TestInvertedFloatComparison {\n   public boolean less(double var1, double var3) {\n      return var1 < var3;// 6\n   }\n\n   public boolean less(int var1, int var2) {\n      return var1 < var2;// 10\n   }\n\n   public boolean notLess(double var1, double var3) {\n      return !(var1 < var3);// 14\n   }\n\n   public boolean notLess(int var1, int var2) {\n      return var1 >= var2;// 18\n   }\n\n   public boolean greater(double var1, double var3) {\n      return var1 > var3;// 22\n   }\n\n   public boolean greater(int var1, int var2) {\n      return var1 > var2;// 26\n   }\n\n   public boolean notGreater(double var1, double var3) {\n      return !(var1 > var3);// 30\n   }\n\n   public boolean notGreater(int var1, int var2) {\n      return var1 <= var2;// 34\n   }\n\n   public boolean lessEqual(double var1, double var3) {\n      return var1 <= var3;// 38\n   }\n\n   public boolean lessEqual(int var1, int var2) {\n      return var1 <= var2;// 42\n   }\n\n   public boolean notLessEqual(double var1, double var3) {\n      return !(var1 <= var3);// 46\n   }\n\n   public boolean notLessEqual(int var1, int var2) {\n      return var1 > var2;// 50\n   }\n\n   public boolean greaterEqual(double var1, double var3) {\n      return var1 >= var3;// 54\n   }\n\n   public boolean greaterEqual(int var1, int var2) {\n      return var1 >= var2;// 58\n   }\n\n   public boolean notGreaterEqual(double var1, double var3) {\n      return !(var1 >= var3);// 62\n   }\n\n   public boolean notGreaterEqual(int var1, int var2) {\n      return var1 < var2;// 66\n   }\n}\n\nclass 'pkg/TestInvertedFloatComparison' {\n   method 'less (DD)Z' {\n      2      4\n      b      4\n   }\n\n   method 'less (II)Z' {\n      2      8\n      a      8\n   }\n\n   method 'notLess (DD)Z' {\n      2      12\n      b      12\n   }\n\n   method 'notLess (II)Z' {\n      2      16\n      a      16\n   }\n\n   method 'greater (DD)Z' {\n      2      20\n      b      20\n   }\n\n   method 'greater (II)Z' {\n      2      24\n      a      24\n   }\n\n   method 'notGreater (DD)Z' {\n      2      28\n      b      28\n   }\n\n   method 'notGreater (II)Z' {\n      2      32\n      a      32\n   }\n\n   method 'lessEqual (DD)Z' {\n      2      36\n      b      36\n   }\n\n   method 'lessEqual (II)Z' {\n      2      40\n      a      40\n   }\n\n   method 'notLessEqual (DD)Z' {\n      2      44\n      b      44\n   }\n\n   method 'notLessEqual (II)Z' {\n      2      48\n      a      48\n   }\n\n   method 'greaterEqual (DD)Z' {\n      2      52\n      b      52\n   }\n\n   method 'greaterEqual (II)Z' {\n      2      56\n      a      56\n   }\n\n   method 'notGreaterEqual (DD)Z' {\n      2      60\n      b      60\n   }\n\n   method 'notGreaterEqual (II)Z' {\n      2      64\n      a      64\n   }\n}\n\nLines mapping:\n6 <-> 5\n10 <-> 9\n14 <-> 13\n18 <-> 17\n22 <-> 21\n26 <-> 25\n30 <-> 29\n34 <-> 33\n38 <-> 37\n42 <-> 41\n46 <-> 45\n50 <-> 49\n54 <-> 53\n58 <-> 57\n62 <-> 61\n66 <-> 65\n"
  },
  {
    "path": "testData/results/TestJava11StringConcat.dec",
    "content": "package java11;\n\npublic class TestJava11StringConcat {\n   public String test1(String var1, int var2) {\n      return var1 + var2;// 20\n   }\n\n   public String test2(String var1, int var2, Object var3) {\n      return \"(\" + var1 + \"-\" + var2 + \"---\" + var3 + \")\";// 24\n   }\n}\n\nclass 'java11/TestJava11StringConcat' {\n   method 'test1 (Ljava/lang/String;I)Ljava/lang/String;' {\n      2      4\n      7      4\n   }\n\n   method 'test2 (Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String;' {\n      3      8\n      8      8\n   }\n}\n\nLines mapping:\n20 <-> 5\n24 <-> 9\n"
  },
  {
    "path": "testData/results/TestJava11StringConcatEmptyAffix.dec",
    "content": "package java11;\n\npublic class TestJava11StringConcatEmptyAffix {\n   public String testEmptyPrefixInt(int value) {\n      return \"\" + value;// 6\n   }\n\n   public String testEmptyPrefixString(String value) {\n      return \"\" + value;// 10\n   }\n\n   public String testPrefixInt(int value) {\n      return \"prefix\" + value;// 14\n   }\n\n   public String testPrefixString(String value) {\n      return \"prefix\" + value;// 18\n   }\n\n   public String testIntEmptySuffix(int value) {\n      return \"\" + value;// 23\n   }\n\n   public String testStringEmptySuffix(String value) {\n      return \"\" + value;// 28\n   }\n\n   public String testIntSuffix(int value) {\n      return \"\" + value + \"suffix\";// 32\n   }\n\n   public String testStringSuffix(String value) {\n      return value + \"suffix\";// 36\n   }\n\n   public String testIntInt(int first, int second) {\n      return \"\" + first + second;// 40\n   }\n\n   public String testIntIntSuffix(int first, int second) {\n      return \"\" + first + second + \"suffix\";// 44\n   }\n\n   public String testIntString(int intValue, String stringValue) {\n      return \"\" + intValue + stringValue;// 48\n   }\n\n   public String testStringInt(int intValue, String stringValue) {\n      return stringValue + intValue;// 52\n   }\n}\n\nclass 'java11/TestJava11StringConcatEmptyAffix' {\n   method 'testEmptyPrefixInt (I)Ljava/lang/String;' {\n      1      4\n      6      4\n   }\n\n   method 'testEmptyPrefixString (Ljava/lang/String;)Ljava/lang/String;' {\n      1      8\n      6      8\n   }\n\n   method 'testPrefixInt (I)Ljava/lang/String;' {\n      1      12\n      6      12\n   }\n\n   method 'testPrefixString (Ljava/lang/String;)Ljava/lang/String;' {\n      1      16\n      6      16\n   }\n\n   method 'testIntEmptySuffix (I)Ljava/lang/String;' {\n      1      20\n      6      20\n   }\n\n   method 'testStringEmptySuffix (Ljava/lang/String;)Ljava/lang/String;' {\n      1      24\n      6      24\n   }\n\n   method 'testIntSuffix (I)Ljava/lang/String;' {\n      1      28\n      6      28\n   }\n\n   method 'testStringSuffix (Ljava/lang/String;)Ljava/lang/String;' {\n      1      32\n      6      32\n   }\n\n   method 'testIntInt (II)Ljava/lang/String;' {\n      2      36\n      7      36\n   }\n\n   method 'testIntIntSuffix (II)Ljava/lang/String;' {\n      2      40\n      7      40\n   }\n\n   method 'testIntString (ILjava/lang/String;)Ljava/lang/String;' {\n      2      44\n      7      44\n   }\n\n   method 'testStringInt (ILjava/lang/String;)Ljava/lang/String;' {\n      2      48\n      7      48\n   }\n}\n\nLines mapping:\n6 <-> 5\n10 <-> 9\n14 <-> 13\n18 <-> 17\n23 <-> 21\n28 <-> 25\n32 <-> 29\n36 <-> 33\n40 <-> 37\n44 <-> 41\n48 <-> 45\n52 <-> 49\n"
  },
  {
    "path": "testData/results/TestJava11StringConcatSpecialChars.dec",
    "content": "package java11;\n\npublic class TestJava11StringConcatSpecialChars {\n   public String testOrdinaryInfix(String first, String second, String last) {\n      return \"BEGIN \" + first + \" (first infix) \" + second + \" (second infix) \" + last + \" END\";// 6\n   }\n\n   public String testSpecialCharsInfix(String first, String second, String last) {\n      return \"BEGIN \" + first + \" (first\\u0001infix) \" + second + \" (second\\u0002infix) \" + last + \" END\";// 10\n   }\n}\n\nclass 'java11/TestJava11StringConcatSpecialChars' {\n   method 'testOrdinaryInfix (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;' {\n      3      4\n      8      4\n   }\n\n   method 'testSpecialCharsInfix (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;' {\n      3      8\n      8      8\n   }\n}\n\nLines mapping:\n6 <-> 5\n10 <-> 9\n"
  },
  {
    "path": "testData/results/TestJava9PrivateInterfaceMethod.dec",
    "content": "package java9;\n\npublic interface TestJava9PrivateInterfaceMethod {\n   private void privateMethod() {\n   }// 4\n}\n\nclass 'java9/TestJava9PrivateInterfaceMethod' {\n   method 'privateMethod ()V' {\n      0      4\n   }\n}\n\nLines mapping:\n4 <-> 5\n"
  },
  {
    "path": "testData/results/TestJava9StringConcat.dec",
    "content": "package java9;\n\npublic class TestJava9StringConcat {\n   public String test1(String var1, int var2) {\n      return var1 + var2;// 20\n   }\n\n   public String test2(String var1, int var2, Object var3) {\n      return \"(\" + var1 + \"-\" + var2 + \"---\" + var3 + \")\";// 24\n   }\n}\n\nclass 'java9/TestJava9StringConcat' {\n   method 'test1 (Ljava/lang/String;I)Ljava/lang/String;' {\n      2      4\n      7      4\n   }\n\n   method 'test2 (Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String;' {\n      3      8\n      8      8\n   }\n}\n\nLines mapping:\n20 <-> 5\n24 <-> 9\n"
  },
  {
    "path": "testData/results/TestKotlinConstructorKt.dec",
    "content": "import java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport kotlin.Metadata;\nimport kotlin.TypeCastException;\nimport kotlin.collections.CollectionsKt;\n\n@Metadata(\n   mv = {1, 1, 1},\n   bv = {1, 0, 0},\n   k = 2,\n   d1 = {\"\\u0000\\u0016\\n\\u0000\\n\\u0002\\u0010 \\n\\u0002\\u0018\\u0002\\n\\u0000\\n\\u0002\\u0010\\u001e\\n\\u0002\\u0010\\u000e\\n\\u0000\\u001a\\u001c\\u0010\\u0000\\u001a\\b\\u0012\\u0004\\u0012\\u00020\\u00020\\u00012\\f\\u0010\\u0003\\u001a\\b\\u0012\\u0004\\u0012\\u00020\\u00050\\u0004H\\u0002¨\\u0006\\u0006\"},\n   d2 = {\"foo\", \"\", \"LMapping;\", \"list\", \"\", \"\", \"module1\"}\n)\npublic final class TestKotlinConstructorKt {\n   private static final List<Mapping> foo(Collection<String> list) {\n      Iterable $receiver$iv = (Iterable)list;// 2\n      Collection destination$iv$iv = (Collection)(new ArrayList(CollectionsKt.collectionSizeOrDefault($receiver$iv, 10)));// 10\n      Iterator var4 = $receiver$iv.iterator();// 11\n\n      while(var4.hasNext()) {\n         Object item$iv$iv = var4.next();\n         String it = (String)item$iv$iv;// 12\n         Mapping var10000 = new Mapping;\n         if (it == null) {// 3\n            throw new TypeCastException(\"null cannot be cast to non-null type kotlin.String\");\n         }\n\n         var10000.<init>((String)it);\n         Mapping var11 = var10000;\n         destination$iv$iv.add(var11);\n      }\n\n      return CollectionsKt.toList((Iterable)((List)destination$iv$iv));// 4 13\n   }\n}\n\nclass 'TestKotlinConstructorKt' {\n   method 'foo (Ljava/util/Collection;)Ljava/util/List;' {\n      1      17\n      4      17\n      d      18\n      f      18\n      15      18\n      18      18\n      1b      19\n      20      19\n      24      21\n      2e      22\n      33      22\n      38      23\n      3b      23\n      46      25\n      4d      26\n      52      26\n      53      29\n      56      29\n      59      30\n      5f      31\n      69      34\n      6c      34\n      6f      34\n      72      34\n   }\n}\n\nLines mapping:\n2 <-> 18\n3 <-> 26\n4 <-> 35\n10 <-> 19\n11 <-> 20\n12 <-> 24\n13 <-> 35\n"
  },
  {
    "path": "testData/results/TestLambdaParams.dec",
    "content": "package pkg;\n\nimport java.util.function.Function;\n\npublic class TestLambdaParams {\n   public static void toCollection(Object var0) {\n      Object var1 = null;// 23\n      Function var2 = (var1x) -> {\n         return var0;\n      };// 24\n      Function var3 = (var1x) -> {\n         return var1;\n      };// 25\n      Function var4 = (var0x) -> {\n         return var0x;\n      };// 26\n   }// 27\n}\n\nclass 'pkg/TestLambdaParams' {\n   method 'lambda$toCollection$0 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;' {\n      1      8\n   }\n\n   method 'lambda$toCollection$1 (Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/Object;' {\n      1      11\n   }\n\n   method 'lambda$toCollection$2 (Ljava/lang/Object;)Ljava/lang/Object;' {\n      1      14\n   }\n\n   method 'toCollection (Ljava/lang/Object;)V' {\n      0      6\n      1      6\n      8      9\n      f      12\n      15      15\n      17      16\n   }\n}\n\nLines mapping:\n23 <-> 7\n24 <-> 10\n25 <-> 13\n26 <-> 16\n27 <-> 17\n"
  },
  {
    "path": "testData/results/TestLocalClass.dec",
    "content": "package pkg;\n\npublic abstract class TestLocalClass {\n   void foo() {\n      boolean var1 = true;// 8\n\n      class Local {\n         void foo() {\n            boolean var1 = true;// 11\n            boolean var2 = true;// 12\n         }// 13\n      }\n\n      Local var2 = new Local();// 15\n      var2.foo();// 16\n   }// 17\n\n   void boo() {\n      boolean var1 = true;// 20\n   }// 21\n\n   void zoo() {\n      boolean var1 = true;// 24\n   }// 25\n}\n\nclass 'pkg/TestLocalClass$1Local' {\n   method 'foo ()V' {\n      0      8\n      1      8\n      2      9\n      3      9\n      4      10\n   }\n}\n\nclass 'pkg/TestLocalClass' {\n   method 'foo ()V' {\n      0      4\n      1      4\n      a      13\n      c      14\n      f      15\n   }\n\n   method 'boo ()V' {\n      0      18\n      1      18\n      2      19\n   }\n\n   method 'zoo ()V' {\n      0      22\n      1      22\n      2      23\n   }\n}\n\nLines mapping:\n8 <-> 5\n11 <-> 9\n12 <-> 10\n13 <-> 11\n15 <-> 14\n16 <-> 15\n17 <-> 16\n20 <-> 19\n21 <-> 20\n24 <-> 23\n25 <-> 24\n"
  },
  {
    "path": "testData/results/TestLocalsNames.dec",
    "content": "package pkg;\n\nimport java.io.File;\n\npublic class TestLocalsNames {\n   private static void rename(File file, boolean recursively) {\n      if (file.isDirectory()) {// 22\n         long start = System.currentTimeMillis();// 23\n         File[] files = file.listFiles();// 25\n         File[] var5 = files;\n         int var6 = files.length;\n\n         for(int var7 = 0; var7 < var6; ++var7) {// 26\n            File s = var5[var7];\n            File dest = new File(s.getAbsolutePath() + \".tmp\");// 27\n\n            assert s.renameTo(dest) : \"unable to rename \" + s + \" to \" + dest;// 28\n         }\n\n         long elapsed = System.currentTimeMillis() - start;// 31\n         System.out.println(\"took \" + elapsed + \"ms (\" + elapsed / (long)files.length + \"ms per dir)\");// 32\n      }\n\n   }// 34\n}\n\nclass 'pkg/TestLocalsNames' {\n   method 'rename (Ljava/io/File;Z)V' {\n      1      6\n      4      6\n      7      7\n      a      7\n      c      8\n      f      8\n      13      9\n      17      10\n      18      10\n      1a      12\n      1b      12\n      21      12\n      28      13\n      29      13\n      38      14\n      3e      14\n      43      14\n      49      14\n      55      16\n      66      16\n      70      16\n      7a      16\n      81      12\n      87      19\n      8b      19\n      8c      19\n      8e      20\n      98      20\n      a2      20\n      ab      20\n      ac      20\n      ad      20\n      b1      20\n      b6      20\n      b9      20\n      bc      23\n   }\n}\n\nLines mapping:\n22 <-> 7\n23 <-> 8\n25 <-> 9\n26 <-> 13\n27 <-> 15\n28 <-> 17\n31 <-> 20\n32 <-> 21\n34 <-> 24\n"
  },
  {
    "path": "testData/results/TestLocalsSignature.dec",
    "content": "package pkg;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TestLocalsSignature {\n   public static void main(String[] args) {\n      List<String> s = new ArrayList();// 24\n      s.add(\"xxx\");// 25\n   }// 26\n}\n\nclass 'pkg/TestLocalsSignature' {\n   method 'main ([Ljava/lang/String;)V' {\n      7      7\n      9      8\n      b      8\n      11      9\n   }\n}\n\nLines mapping:\n24 <-> 8\n25 <-> 9\n26 <-> 10\n"
  },
  {
    "path": "testData/results/TestMemberAnnotations.dec",
    "content": "package pkg;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nclass TestMemberAnnotations {\n   @TestMemberAnnotations.A(\"const\")\n   public static final int CONST = 42;\n   @TestMemberAnnotations.A(\"field\")\n   private int f;\n\n   @TestMemberAnnotations.A(\"return\")\n   private int f(@TestMemberAnnotations.A(\"arg\") int var1) {\n      return var1 + this.f + 42;// 12\n   }\n\n   @Retention(RetentionPolicy.RUNTIME)\n   @interface A {\n      String value() default \"\";\n   }\n}\n\nclass 'pkg/TestMemberAnnotations' {\n   method 'f (I)I' {\n      2      13\n      5      13\n      6      13\n      8      13\n      9      13\n   }\n}\n\nLines mapping:\n12 <-> 14\n"
  },
  {
    "path": "testData/results/TestMethodParameters.dec",
    "content": "package pkg;\n\npublic class TestMethodParameters {\n   TestMethodParameters(@Deprecated int var1) {\n   }// 19\n\n   void m1(@Deprecated int var1) {\n   }// 20\n\n   static void m2(@Deprecated int var0) {\n   }// 21\n\n   void local() {\n      class Local {\n         Local(@Deprecated int var2) {\n         }// 36\n\n         void m(@Deprecated int var1) {\n         }// 37\n      }\n\n   }// 39\n\n   static class C2 {\n      C2(@Deprecated int var1) {\n      }// 29\n\n      void m1(@Deprecated int var1) {\n      }// 30\n\n      static void m2(@Deprecated int var0) {\n      }// 31\n   }\n\n   class C1 {\n      C1(@Deprecated int var2) {\n      }// 24\n\n      void m(@Deprecated int var1) {\n      }// 25\n   }\n}\n\nclass 'pkg/TestMethodParameters' {\n   method '<init> (I)V' {\n      4      4\n   }\n\n   method 'm1 (I)V' {\n      0      7\n   }\n\n   method 'm2 (I)V' {\n      0      10\n   }\n\n   method 'local ()V' {\n      0      21\n   }\n}\n\nclass 'pkg/TestMethodParameters$1Local' {\n   method '<init> (Lpkg/TestMethodParameters;I)V' {\n      9      15\n   }\n\n   method 'm (I)V' {\n      0      18\n   }\n}\n\nclass 'pkg/TestMethodParameters$C2' {\n   method '<init> (I)V' {\n      4      25\n   }\n\n   method 'm1 (I)V' {\n      0      28\n   }\n\n   method 'm2 (I)V' {\n      0      31\n   }\n}\n\nclass 'pkg/TestMethodParameters$C1' {\n   method '<init> (Lpkg/TestMethodParameters;I)V' {\n      9      36\n   }\n\n   method 'm (I)V' {\n      0      39\n   }\n}\n\nLines mapping:\n19 <-> 5\n20 <-> 8\n21 <-> 11\n24 <-> 37\n25 <-> 40\n29 <-> 26\n30 <-> 29\n31 <-> 32\n36 <-> 16\n37 <-> 19\n39 <-> 22\n"
  },
  {
    "path": "testData/results/TestMethodParametersAttr.dec",
    "content": "package pkg;\n\npublic class TestMethodParametersAttr {\n   TestMethodParametersAttr(int p01) {\n      System.out.print(p01);// 5\n   }\n\n   void m1(int p02) {\n      System.out.print(p02);// 6\n   }\n\n   static void m2(int p03) {\n      System.out.print(p03);// 7\n   }\n\n   void local() {\n      class Local {\n         Local(int p31) {\n            System.out.print(p31);\n         }// 22\n\n         void m(int p32) {\n            System.out.print(p32);// 23\n         }\n      }\n\n   }// 25\n\n   static enum E1 {\n      private E1(int p71) {\n         System.out.print(p71);// 44\n      }\n   }\n\n   abstract static class C4 {\n      abstract void m1(int p61);\n\n      abstract void m2(final int p62);\n   }\n\n   abstract class C3 {\n      abstract void m1(int p51);\n\n      abstract void m2(final int p52);\n   }\n\n   interface I1 {\n      void m1(int p41);\n\n      void m2(final int p42);\n   }\n\n   static class C2 {\n      C2(int p21) {\n         System.out.print(p21);// 15\n      }\n\n      void m1(int p22) {\n         System.out.print(p22);// 16\n      }\n\n      static void m2(int p23) {\n         System.out.print(p23);// 17\n      }\n   }\n\n   class C1 {\n      C1(int p11) {\n         System.out.print(p11);\n      }// 10\n\n      void m(int p12) {\n         System.out.print(p12);// 11\n      }\n   }\n}\n\nclass 'pkg/TestMethodParametersAttr' {\n   method '<init> (I)V' {\n      4      4\n      8      4\n      b      5\n   }\n\n   method 'm1 (I)V' {\n      0      8\n      4      8\n      7      9\n   }\n\n   method 'm2 (I)V' {\n      0      12\n      4      12\n      7      13\n   }\n\n   method 'local ()V' {\n      0      26\n   }\n}\n\nclass 'pkg/TestMethodParametersAttr$1Local' {\n   method '<init> (Lpkg/TestMethodParametersAttr;I)V' {\n      9      18\n      d      18\n      10      19\n   }\n\n   method 'm (I)V' {\n      0      22\n      4      22\n      7      23\n   }\n}\n\nclass 'pkg/TestMethodParametersAttr$E1' {\n   method '<init> (Ljava/lang/String;II)V' {\n      6      30\n      a      30\n      d      31\n   }\n}\n\nclass 'pkg/TestMethodParametersAttr$C2' {\n   method '<init> (I)V' {\n      4      54\n      8      54\n      b      55\n   }\n\n   method 'm1 (I)V' {\n      0      58\n      4      58\n      7      59\n   }\n\n   method 'm2 (I)V' {\n      0      62\n      4      62\n      7      63\n   }\n}\n\nclass 'pkg/TestMethodParametersAttr$C1' {\n   method '<init> (Lpkg/TestMethodParametersAttr;I)V' {\n      9      68\n      d      68\n      10      69\n   }\n\n   method 'm (I)V' {\n      0      72\n      4      72\n      7      73\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 9\n7 <-> 13\n10 <-> 70\n11 <-> 73\n15 <-> 55\n16 <-> 59\n17 <-> 63\n22 <-> 20\n23 <-> 23\n25 <-> 27\n44 <-> 31\n"
  },
  {
    "path": "testData/results/TestMethodReferenceLetterClass.dec",
    "content": "import java.util.function.Consumer;\n\npublic class TestMethodReferenceLetterClass {\n   void boo() {\n      Consumer var1 = R::foo;// 21\n   }// 22\n}\n\nclass 'TestMethodReferenceLetterClass' {\n   method 'boo ()V' {\n      5      4\n      6      5\n   }\n}\n\nLines mapping:\n21 <-> 5\n22 <-> 6\n"
  },
  {
    "path": "testData/results/TestMethodReferenceSameName.dec",
    "content": "public class TestMethodReferenceSameName {\n   R1 r;\n\n   private void foo() {\n      R1 var10000 = this.r;// 5\n      (var10000::foo).run();\n   }// 6\n\n   class R1 {\n      void foo() {\n      }// 9\n   }\n}\n\nclass 'TestMethodReferenceSameName' {\n   method 'foo ()V' {\n      1      4\n      e      5\n      13      6\n   }\n}\n\nclass 'TestMethodReferenceSameName$R1' {\n   method 'foo ()V' {\n      0      10\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 7\n9 <-> 11\n"
  },
  {
    "path": "testData/results/TestMissingConstructorCallBad.dec",
    "content": "package pkg;\n\npublic class TestMissingConstructorCallBad {\n   private TestMissingConstructorCallBad() {\n      System.out.println(\"Nobody will see what we do here!\");// 14 15 16\n      this((Object)null);// 19 20\n   }// 21\n\n   public static void main(String... var0) {\n      try {\n         new TestMissingConstructorCallBad();\n      } catch (Throwable var2) {// 37\n      }\n\n   }// 39\n}\n\nclass 'pkg/TestMissingConstructorCallBad' {\n   method '<init> ()V' {\n      0      4\n      3      4\n      5      4\n      9      5\n      a      5\n      d      6\n   }\n\n   method 'main ([Ljava/lang/String;)V' {\n      b      11\n      c      14\n   }\n}\n\nLines mapping:\n14 <-> 5\n15 <-> 5\n16 <-> 5\n19 <-> 6\n20 <-> 6\n21 <-> 7\n37 <-> 12\n39 <-> 15\nNot mapped:\n18\n28\n29\n30\n31\n33\n"
  },
  {
    "path": "testData/results/TestMissingConstructorCallGood.dec",
    "content": "package pkg;\n\npublic class TestMissingConstructorCallGood {\n   private TestMissingConstructorCallGood(Object var1) {\n   }// 16\n\n   private TestMissingConstructorCallGood() {\n      System.out.println(\"Nobody will see what we do here!\");// 22 23 24\n      this((Object)null);// 27 28\n   }// 29\n\n   public static void main(String... var0) {\n      try {\n         new TestMissingConstructorCallGood();\n      } catch (Throwable var2) {// 45\n      }\n\n   }// 47\n}\n\nclass 'pkg/TestMissingConstructorCallGood' {\n   method '<init> (Ljava/lang/Object;)V' {\n      4      4\n   }\n\n   method '<init> ()V' {\n      0      7\n      3      7\n      5      7\n      9      8\n      a      8\n      d      9\n   }\n\n   method 'main ([Ljava/lang/String;)V' {\n      b      14\n      c      17\n   }\n}\n\nLines mapping:\n16 <-> 5\n22 <-> 8\n23 <-> 8\n24 <-> 8\n27 <-> 9\n28 <-> 9\n29 <-> 10\n45 <-> 15\n47 <-> 18\nNot mapped:\n14\n15\n26\n36\n37\n38\n39\n41\n"
  },
  {
    "path": "testData/results/TestNamedSuspendFun2Kt.dec",
    "content": "import kotlin.Metadata;\nimport kotlin.SuccessOrFailure;\nimport kotlin.coroutines.Continuation;\nimport kotlin.coroutines.intrinsics.IntrinsicsKt;\nimport kotlin.coroutines.jvm.internal.ContinuationImpl;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\n@Metadata(\n   mv = {1, 1, 11},\n   bv = {1, 0, 2},\n   k = 2,\n   xi = 2,\n   d1 = {\"\\u0000\\n\\n\\u0000\\n\\u0002\\u0010\\b\\n\\u0002\\b\\u0002\\u001a\\u0011\\u0010\\u0000\\u001a\\u00020\\u0001H\\u0086@ø\\u0001\\u0000¢\\u0006\\u0002\\u0010\\u0002\\u001a\\u0011\\u0010\\u0003\\u001a\\u00020\\u0001H\\u0086@ø\\u0001\\u0000¢\\u0006\\u0002\\u0010\\u0002\\u0082\\u0002\\u0004\\n\\u0002\\b\\u0019\"},\n   d2 = {\"bar\", \"\", \"(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;\", \"foo2\"}\n)\npublic final class TestNamedSuspendFun2Kt {\n   @Nullable\n   public static final Object foo2(@NotNull Continuation<? super Integer> var0) {\n      @Metadata(\n         mv = {1, 1, 11},\n         bv = {1, 0, 2},\n         k = 3,\n         xi = 2,\n         d1 = {\"\\u0000\\u0010\\n\\u0000\\n\\u0002\\u0010\\u0000\\n\\u0000\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\b\\u0010\\u0000\\u001a\\u0004\\u0018\\u00010\\u00012\\f\\u0010\\u0002\\u001a\\b\\u0012\\u0004\\u0012\\u00020\\u00040\\u0003H\\u0086@ø\\u0001\\u0000\"},\n         d2 = {\"foo2\", \"\", \"continuation\", \"Lkotlin/coroutines/Continuation;\", \"\"}\n      )\n      final class NamelessClass_1 extends ContinuationImpl {\n         int label;\n         int I$0;\n         Object L$0;\n\n         @Nullable\n         public final Object invokeSuspend(@NotNull Object result) {\n            this.data = result;\n            this.label |= -2147483648;\n            return TestNamedSuspendFun2Kt.foo2(this);\n         }\n\n         NamelessClass_1(Continuation var1) {\n            super(var1);\n         }\n      }\n\n      NamelessClass_1 var3;\n      label463: {\n         if (var0 instanceof NamelessClass_1) {\n            var3 = (NamelessClass_1)var0;\n            if ((var3.getLabel() & -2147483648) != 0) {\n               var3.setLabel(var3.getLabel() - -2147483648);\n               break label463;\n            }\n         }\n\n         var3 = new NamelessClass_1(var0);\n      }\n\n      Object var4;\n      int x;\n      label491: {\n         Throwable var1;\n         Throwable var10000;\n         label472: {\n            Object var2 = var3.data;\n            var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();// 2\n            boolean var10001;\n            Object var22;\n            switch (var3.label) {\n               case 0:\n                  if (var2 instanceof SuccessOrFailure.Failure) {\n                     throw ((SuccessOrFailure.Failure)var2).exception;\n                  }\n                  break;\n               case 1:\n                  try {\n                     if (var2 instanceof SuccessOrFailure.Failure) {\n                        throw ((SuccessOrFailure.Failure)var2).exception;\n                     }\n\n                     var22 = var2;\n                  } catch (Throwable var19) {\n                     var10000 = var19;\n                     var10001 = false;\n                     break label472;\n                  }\n\n                  try {\n                     x = ((Number)var22).intValue();// 6\n                     if (x == 0) {\n                        break label491;\n                     }\n                  } catch (Throwable var17) {\n                     var10000 = var17;\n                     var10001 = false;\n                     break label472;\n                  }\n\n                  var3.label = 3;\n                  if (bar(var3) == var4) {\n                     return var4;\n                  }\n                  break;\n               case 2:\n                  x = var3.I$0;\n                  if (var2 instanceof SuccessOrFailure.Failure) {\n                     throw ((SuccessOrFailure.Failure)var2).exception;\n                  }\n\n                  return 1;// 11\n               case 3:\n                  if (var2 instanceof SuccessOrFailure.Failure) {\n                     throw ((SuccessOrFailure.Failure)var2).exception;\n                  }\n                  break;\n               case 4:\n                  var1 = (Throwable)var3.L$0;\n                  if (var2 instanceof SuccessOrFailure.Failure) {\n                     throw ((SuccessOrFailure.Failure)var2).exception;\n                  }\n\n                  throw var1;// 9\n               default:\n                  throw new IllegalStateException(\"call to 'resume' before 'invoke' with coroutine\");\n            }\n\n            do {\n               try {\n                  var3.label = 1;// 5\n                  var22 = bar(var3);\n               } catch (Throwable var18) {\n                  var10000 = var18;\n                  var10001 = false;\n                  break label472;\n               }\n\n               if (var22 == var4) {\n                  return var4;\n               }\n\n               try {\n                  x = ((Number)var22).intValue();\n                  if (x == 0) {\n                     break label491;\n                  }\n               } catch (Throwable var20) {\n                  var10000 = var20;\n                  var10001 = false;\n                  break label472;\n               }\n\n               var3.label = 3;\n            } while(bar(var3) != var4);\n\n            return var4;\n         }\n\n         var1 = var10000;\n         var3.L$0 = var1;\n         var3.label = 4;\n         if (bar(var3) == var4) {// 8\n            return var4;\n         }\n\n         throw var1;\n      }\n\n      var3.I$0 = x;\n      var3.label = 2;\n      if (bar(var3) == var4) {\n         return var4;\n      } else {\n         return 1;\n      }\n   }\n\n   @Nullable\n   public static final Object bar(@NotNull Continuation<? super Integer> var0) {\n      return 0;// 14\n   }\n}\n\nclass 'TestNamedSuspendFun2Kt$foo2$1' {\n   method 'invokeSuspend (Ljava/lang/Object;)Ljava/lang/Object;' {\n      2      34\n      a      35\n      d      35\n      11      36\n      14      36\n   }\n\n   method '<init> (Lkotlin/coroutines/Continuation;)V' {\n      2      40\n      5      41\n   }\n}\n\nclass 'TestNamedSuspendFun2Kt' {\n   method 'foo2 (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;' {\n      1      46\n      4      46\n      8      47\n      b      47\n      d      48\n      10      48\n      12      48\n      13      48\n      18      49\n      1b      49\n      1d      49\n      1e      49\n      21      50\n      2c      54\n      2e      63\n      31      63\n      32      64\n      35      64\n      38      67\n      3b      67\n      5e      69\n      61      69\n      64      70\n      67      70\n      6a      70\n      6f      127\n      70      127\n      73      128\n      79      135\n      7e      136\n      81      75\n      84      75\n      87      76\n      8a      76\n      8d      76\n      90      87\n      93      87\n      96      87\n      98      88\n      9e      166\n      a2      167\n      a3      167\n      a6      168\n      ac      168\n      b1      169\n      b3      103\n      b6      103\n      b9      104\n      bc      104\n      bf      105\n      c2      105\n      c5      105\n      c9      108\n      ce      97\n      cf      97\n      d2      98\n      d8      98\n      dd      99\n      e0      110\n      e3      110\n      e6      111\n      e9      111\n      ec      111\n      f3      156\n      f7      157\n      fb      158\n      fc      158\n      ff      159\n      105      159\n      10a      160\n      10c      115\n      10f      115\n      112      115\n      115      116\n      118      116\n      11b      117\n      11e      117\n      121      117\n      126      120\n      12a      108\n      12b      108\n      133      122\n      138      122\n   }\n\n   method 'bar (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;' {\n      0      177\n      1      177\n      4      177\n   }\n}\n\nLines mapping:\n2 <-> 65\n5 <-> 128\n6 <-> 88\n8 <-> 160\n9 <-> 121\n11 <-> 109\n14 <-> 178\nNot mapped:\n3\n4\n"
  },
  {
    "path": "testData/results/TestParameterizedTypes.dec",
    "content": "package pkg;\n\npublic abstract class TestParameterizedTypes<P> {\n   abstract TestParameterizedTypes<P>.Inner<String> getUnspecificInner();\n\n   abstract TestParameterizedTypes<Number>.Inner<String> getSpecificInner();\n\n   public abstract class Inner<I> {\n   }\n}\n\n"
  },
  {
    "path": "testData/results/TestPop2OneDoublePop2.dec",
    "content": "package pkg;\n\npublic class TestPop2OneDoublePop2 {\n   public static void main(String... var0) {\n      double var10002 = 3.14159265358;// 24\n      System.out.println(1234567890);// 22 23 26\n   }// 27\n}\n\nclass 'pkg/TestPop2OneDoublePop2' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      5\n      3      5\n      5      4\n      9      5\n      c      6\n   }\n}\n\nLines mapping:\n22 <-> 6\n23 <-> 6\n24 <-> 5\n26 <-> 6\n27 <-> 7\nNot mapped:\n25\n"
  },
  {
    "path": "testData/results/TestPop2OneLongPop2.dec",
    "content": "package pkg;\n\npublic class TestPop2OneLongPop2 {\n   public static void main(String... var0) {\n      long var10002 = -889275714L;// 24\n      System.out.println(1234567890);// 22 23 26\n   }// 27\n}\n\nclass 'pkg/TestPop2OneLongPop2' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      5\n      3      5\n      5      4\n      9      5\n      c      6\n   }\n}\n\nLines mapping:\n22 <-> 6\n23 <-> 6\n24 <-> 5\n26 <-> 6\n27 <-> 7\nNot mapped:\n25\n"
  },
  {
    "path": "testData/results/TestPop2TwoIntPop2.dec",
    "content": "package pkg;\n\npublic class TestPop2TwoIntPop2 {\n   public static void main(String... var0) {\n      char var10002 = '쫾';// 24\n      char var10003 = '몾';// 25\n      System.out.println(1234567890);// 22 23 27\n   }// 28\n}\n\nclass 'pkg/TestPop2TwoIntPop2' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      6\n      3      6\n      5      4\n      7      5\n      a      6\n      d      7\n   }\n}\n\nLines mapping:\n22 <-> 7\n23 <-> 7\n24 <-> 5\n25 <-> 6\n27 <-> 7\n28 <-> 8\nNot mapped:\n26\n"
  },
  {
    "path": "testData/results/TestPop2TwoIntTwoPop.dec",
    "content": "package pkg;\n\npublic class TestPop2TwoIntTwoPop {\n   public static void main(String... var0) {\n      char var10002 = '쫾';// 24\n      char var10003 = '몾';// 25\n      System.out.println(1234567890);// 22 23 28\n   }// 29\n}\n\nclass 'pkg/TestPop2TwoIntTwoPop' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      6\n      3      6\n      5      4\n      7      5\n      b      6\n      e      7\n   }\n}\n\nLines mapping:\n22 <-> 7\n23 <-> 7\n24 <-> 5\n25 <-> 6\n28 <-> 7\n29 <-> 8\nNot mapped:\n26\n27\n"
  },
  {
    "path": "testData/results/TestPrimitiveNarrowing.dec",
    "content": "package pkg;\n\nclass TestPrimitiveNarrowing {\n   TestPrimitiveNarrowing(Short value) {\n   }// 6\n\n   static void invocations() {\n      withInteger((Integer)null);// 9\n      withShort((Short)null);// 10\n      withByte((Byte)null);// 11\n      new TestPrimitiveNarrowing((Short)null);// 12\n   }// 13\n\n   static void withByte(Byte defaultValue) {\n   }// 16\n\n   static void withInteger(Integer defaultValue) {\n   }// 19\n\n   static void withShort(Short defaultValue) {\n   }// 22\n}\n\nclass 'pkg/TestPrimitiveNarrowing' {\n   method '<init> (Ljava/lang/Short;)V' {\n      4      4\n   }\n\n   method 'invocations ()V' {\n      0      7\n      1      7\n      4      8\n      5      8\n      8      9\n      9      9\n      10      10\n      15      11\n   }\n\n   method 'withByte (Ljava/lang/Byte;)V' {\n      0      14\n   }\n\n   method 'withInteger (Ljava/lang/Integer;)V' {\n      0      17\n   }\n\n   method 'withShort (Ljava/lang/Short;)V' {\n      0      20\n   }\n}\n\nLines mapping:\n6 <-> 5\n9 <-> 8\n10 <-> 9\n11 <-> 10\n12 <-> 11\n13 <-> 12\n16 <-> 15\n19 <-> 18\n22 <-> 21\nNot mapped:\n5\n"
  },
  {
    "path": "testData/results/TestPrimitives.dec",
    "content": "package pkg;\n\nimport java.util.HashMap;\n\npublic class TestPrimitives {\n   public void printAll() {\n      this.printBoolean(true);// 8\n      this.printByte((byte)123);// 9\n      this.printShort((short)257);// 10\n      this.printInt(123);// 11\n      this.printLong(123L);// 12\n      this.printFloat(1.23F);// 13\n      this.printDouble(1.23);// 14\n      this.printChar('Z');// 15\n      this.printBooleanBoxed(true);// 17\n      this.printByteBoxed((byte)123);// 18\n      this.printShortBoxed((short)257);// 19\n      this.printIntBoxed(1);// 20\n      this.printIntBoxed(40000);// 21\n      this.printLongBoxed(123L);// 22\n      this.printFloatBoxed(1.23F);// 23\n      this.printDoubleBoxed(1.23);// 24\n      this.printCharBoxed('Z');// 25\n      this.printBoolean(Boolean.valueOf(\"true\"));// 27\n      this.printByte(Byte.valueOf(\"123\"));// 28\n      this.printShort(Short.valueOf(\"257\"));// 29\n      this.printInt(Integer.valueOf(\"123\"));// 30\n      this.printLong(Long.valueOf(\"123\"));// 31\n      this.printFloat(Float.valueOf(\"1.23\"));// 32\n      this.printDouble(Double.valueOf(\"1.23\"));// 33\n      this.printChar(new Character('Z'));// 34\n      this.printInt(this.getInteger());// 36\n      this.printChar(this.getCharacter());// 37\n      System.out.printf(\"%b, %d, %d, %d, %c, %d\", true, 1, 213, 40000, 'c', 42L);// 39\n      System.out.printf(\"%b, %d, %d, %d\", this.getBoolean(), this.getByte(), this.getShort(), this.getInt());// 40\n      new TestPrimitives(false, (byte)123, (short)257, 40000, 123L, 3.14F, 1.618, 'A');// 42\n      new TestPrimitives('A', 1.618, 3.14F, 123L, 40000, (short)257, (byte)123, false);// 43\n      new TestPrimitives(Boolean.valueOf(\"false\"), Byte.valueOf(\"123\"), Short.valueOf(\"257\"), Integer.valueOf(\"40000\"), Long.valueOf(\"123\"), Float.valueOf(\"3.14\"), Double.valueOf(\"1.618\"), new Character('A'));// 44 45\n   }// 46\n\n   private TestPrimitives(boolean bool, byte b, short s, int i, long l, float f, double d, char c) {\n      System.out.printf(\"%b, %d, %d, %d, %d, %.2f, %.2f, %c\", bool, b, s, i, l, f, d, c);// 49\n   }// 50\n\n   private TestPrimitives(Character c, Double d, Float f, Long l, Integer i, Short s, Byte b, Boolean bool) {\n      System.out.printf(\"%b, %d, %d, %d, %d, %.2f, %.2f, %c\", bool, b, s, i, l, f, d, c);// 53\n   }// 54\n\n   public void printBoolean(boolean b) {\n      System.out.printf(\"%b\", b);// 57\n   }// 58\n\n   public void printByte(byte b) {\n      System.out.printf(\"%d\", b);// 61\n   }// 62\n\n   public void printShort(short s) {\n      System.out.printf(\"%d\", s);// 65\n   }// 66\n\n   public void printInt(int i) {\n      System.out.printf(\"%d\", i);// 69\n   }// 70\n\n   public void printLong(long l) {\n      System.out.printf(\"%d\", l);// 73\n   }// 74\n\n   public void printFloat(float f) {\n      System.out.printf(\"%f\", f);// 77\n   }// 78\n\n   public void printDouble(double d) {\n      System.out.printf(\"%f\", d);// 81\n   }// 82\n\n   public void printChar(char c) {\n      System.out.printf(\"%c\", c);// 85\n   }// 86\n\n   public void printBooleanBoxed(Boolean b) {\n      System.out.printf(\"%b\", b);// 90\n   }// 91\n\n   public void printByteBoxed(Byte b) {\n      System.out.printf(\"%d\", b);// 94\n   }// 95\n\n   public void printShortBoxed(Short s) {\n      System.out.printf(\"%d\", s);// 98\n   }// 99\n\n   public void printIntBoxed(Integer i) {\n      System.out.printf(\"%d\", i);// 102\n   }// 103\n\n   public void printLongBoxed(Long l) {\n      System.out.printf(\"%d\", l);// 106\n   }// 107\n\n   public void printFloatBoxed(Float f) {\n      System.out.printf(\"%f\", f);// 110\n   }// 111\n\n   public void printDoubleBoxed(Double d) {\n      System.out.printf(\"%f\", d);// 114\n   }// 115\n\n   public void printCharBoxed(Character c) {\n      System.out.printf(\"%c\", c);// 118\n   }// 119\n\n   public boolean getBoolean() {\n      return false;// 123\n   }\n\n   public byte getByte() {\n      return -128;// 127\n   }\n\n   public short getShort() {\n      return -32768;// 131\n   }\n\n   public int getInt() {\n      return 42;// 135\n   }\n\n   public Integer getInteger() {\n      return 40000;// 139\n   }\n\n   public Character getCharacter() {\n      return 'Z';// 143\n   }\n\n   public void printNarrowed() {\n      this.printByte((byte)this.getInt());// 147\n      this.printShort((short)this.getInt());// 148\n   }// 149\n\n   public void constructor() {\n      new Byte((byte)1);// 152\n   }// 153\n\n   private boolean compare(char c) {\n      boolean res = c > -1;// 156\n      res = c > 0;// 157\n      res = c > 1;// 158\n      res = c > '\\b';// 159\n      res = c > '\\t';// 160\n      res = c > '\\n';// 161\n      res = c > '\\f';// 162\n      res = c > '\\r';// 163\n      res = c > ' ';// 164\n      res = c > 'a';// 165\n      res = c > 'Z';// 166\n      res = c > 127;// 167\n      res = c > 255;// 168\n      return res;// 169\n   }\n\n   void testAutoBoxingCallRequired(boolean value) {\n      Boolean.valueOf(value).hashCode();// 173\n   }// 174\n\n   void testCastRequired() {\n      HashMap<String, Byte> map = new HashMap();// 177\n      map.put(\"test\", (byte)0);// 178\n   }// 179\n}\n\nclass 'pkg/TestPrimitives' {\n   method 'printAll ()V' {\n      1      6\n      2      6\n      6      7\n      8      7\n      c      8\n      f      8\n      13      9\n      15      9\n      19      10\n      1c      10\n      20      11\n      22      11\n      26      12\n      29      12\n      2d      13\n      2f      13\n      33      14\n      37      14\n      3b      15\n      40      15\n      44      16\n      4a      16\n      4e      17\n      52      17\n      56      18\n      5b      18\n      5f      19\n      65      19\n      69      20\n      6e      20\n      72      21\n      78      21\n      7c      22\n      81      22\n      85      23\n      87      23\n      8a      23\n      8d      23\n      91      24\n      93      24\n      96      24\n      99      24\n      9d      25\n      9f      25\n      a2      25\n      a5      25\n      a9      26\n      ab      26\n      ae      26\n      b1      26\n      b5      27\n      b7      27\n      ba      27\n      bd      27\n      c1      28\n      c3      28\n      c6      28\n      c9      28\n      cd      29\n      cf      29\n      d2      29\n      d5      29\n      dd      30\n      e2      30\n      e5      30\n      ea      31\n      ed      31\n      f0      31\n      f5      32\n      f8      32\n      fb      32\n      fe      33\n      101      33\n      10a      33\n      10b      33\n      111      33\n      112      33\n      118      33\n      11b      33\n      121      33\n      123      33\n      129      33\n      12b      33\n      131      33\n      134      33\n      138      33\n      13c      34\n      13f      34\n      148      34\n      14b      34\n      152      34\n      155      34\n      15c      34\n      15f      34\n      166      34\n      169      34\n      16d      34\n      175      35\n      176      35\n      178      35\n      17b      35\n      17d      35\n      180      35\n      182      35\n      185      35\n      18f      36\n      194      36\n      19a      36\n      19f      36\n      1a5      36\n      1aa      36\n      1b0      36\n      1b5      36\n      1c1      37\n      1c3      37\n      1c6      37\n      1c9      37\n      1cb      37\n      1ce      37\n      1d1      37\n      1d3      37\n      1d6      37\n      1d9      37\n      1db      37\n      1de      37\n      1e1      37\n      1e3      37\n      1e6      37\n      1e9      37\n      1eb      37\n      1ee      37\n      1f1      37\n      1f3      37\n      1f6      37\n      1fd      37\n      202      37\n      209      38\n   }\n\n   method '<init> (ZBSIJFDC)V' {\n      4      41\n      7      41\n      11      41\n      18      41\n      1f      41\n      27      41\n      2f      41\n      37      41\n      40      41\n      49      41\n      4d      41\n      51      42\n   }\n\n   method '<init> (Ljava/lang/Character;Ljava/lang/Double;Ljava/lang/Float;Ljava/lang/Long;Ljava/lang/Integer;Ljava/lang/Short;Ljava/lang/Byte;Ljava/lang/Boolean;)V' {\n      4      45\n      7      45\n      35      45\n      39      46\n   }\n\n   method 'printBoolean (Z)V' {\n      0      49\n      3      49\n      c      49\n      10      49\n      14      50\n   }\n\n   method 'printByte (B)V' {\n      0      53\n      3      53\n      c      53\n      10      53\n      14      54\n   }\n\n   method 'printShort (S)V' {\n      0      57\n      3      57\n      c      57\n      10      57\n      14      58\n   }\n\n   method 'printInt (I)V' {\n      0      61\n      3      61\n      c      61\n      10      61\n      14      62\n   }\n\n   method 'printLong (J)V' {\n      0      65\n      3      65\n      c      65\n      10      65\n      14      66\n   }\n\n   method 'printFloat (F)V' {\n      0      69\n      3      69\n      c      69\n      10      69\n      14      70\n   }\n\n   method 'printDouble (D)V' {\n      0      73\n      3      73\n      c      73\n      10      73\n      14      74\n   }\n\n   method 'printChar (C)V' {\n      0      77\n      3      77\n      c      77\n      10      77\n      14      78\n   }\n\n   method 'printBooleanBoxed (Ljava/lang/Boolean;)V' {\n      0      81\n      3      81\n      d      81\n      11      82\n   }\n\n   method 'printByteBoxed (Ljava/lang/Byte;)V' {\n      0      85\n      3      85\n      d      85\n      11      86\n   }\n\n   method 'printShortBoxed (Ljava/lang/Short;)V' {\n      0      89\n      3      89\n      d      89\n      11      90\n   }\n\n   method 'printIntBoxed (Ljava/lang/Integer;)V' {\n      0      93\n      3      93\n      d      93\n      11      94\n   }\n\n   method 'printLongBoxed (Ljava/lang/Long;)V' {\n      0      97\n      3      97\n      d      97\n      11      98\n   }\n\n   method 'printFloatBoxed (Ljava/lang/Float;)V' {\n      0      101\n      3      101\n      d      101\n      11      102\n   }\n\n   method 'printDoubleBoxed (Ljava/lang/Double;)V' {\n      0      105\n      3      105\n      d      105\n      11      106\n   }\n\n   method 'printCharBoxed (Ljava/lang/Character;)V' {\n      0      109\n      3      109\n      d      109\n      11      110\n   }\n\n   method 'getBoolean ()Z' {\n      0      113\n      1      113\n   }\n\n   method 'getByte ()B' {\n      0      117\n      2      117\n   }\n\n   method 'getShort ()S' {\n      0      121\n      3      121\n   }\n\n   method 'getInt ()I' {\n      0      125\n      2      125\n   }\n\n   method 'getInteger ()Ljava/lang/Integer;' {\n      0      129\n      2      129\n      5      129\n   }\n\n   method 'getCharacter ()Ljava/lang/Character;' {\n      0      133\n      2      133\n      5      133\n   }\n\n   method 'printNarrowed ()V' {\n      2      137\n      5      137\n      6      137\n      b      138\n      e      138\n      f      138\n      12      139\n   }\n\n   method 'constructor ()V' {\n      4      142\n      9      143\n   }\n\n   method 'compare (C)Z' {\n      1      146\n      2      146\n      a      146\n      c      147\n      14      147\n      16      148\n      17      148\n      1f      148\n      21      149\n      23      149\n      2b      149\n      2d      150\n      2f      150\n      37      150\n      39      151\n      3b      151\n      43      151\n      45      152\n      47      152\n      4f      152\n      51      153\n      53      153\n      5b      153\n      5d      154\n      5f      154\n      67      154\n      69      155\n      6b      155\n      73      155\n      75      156\n      77      156\n      7f      156\n      81      157\n      83      157\n      8b      157\n      8d      158\n      90      158\n      98      158\n      9a      159\n   }\n\n   method 'testAutoBoxingCallRequired (Z)V' {\n      1      163\n      4      163\n      8      164\n   }\n\n   method 'testCastRequired ()V' {\n      7      167\n      9      168\n      b      168\n      f      168\n      13      169\n   }\n}\n\nLines mapping:\n8 <-> 7\n9 <-> 8\n10 <-> 9\n11 <-> 10\n12 <-> 11\n13 <-> 12\n14 <-> 13\n15 <-> 14\n17 <-> 15\n18 <-> 16\n19 <-> 17\n20 <-> 18\n21 <-> 19\n22 <-> 20\n23 <-> 21\n24 <-> 22\n25 <-> 23\n27 <-> 24\n28 <-> 25\n29 <-> 26\n30 <-> 27\n31 <-> 28\n32 <-> 29\n33 <-> 30\n34 <-> 31\n36 <-> 32\n37 <-> 33\n39 <-> 34\n40 <-> 35\n42 <-> 36\n43 <-> 37\n44 <-> 38\n45 <-> 38\n46 <-> 39\n49 <-> 42\n50 <-> 43\n53 <-> 46\n54 <-> 47\n57 <-> 50\n58 <-> 51\n61 <-> 54\n62 <-> 55\n65 <-> 58\n66 <-> 59\n69 <-> 62\n70 <-> 63\n73 <-> 66\n74 <-> 67\n77 <-> 70\n78 <-> 71\n81 <-> 74\n82 <-> 75\n85 <-> 78\n86 <-> 79\n90 <-> 82\n91 <-> 83\n94 <-> 86\n95 <-> 87\n98 <-> 90\n99 <-> 91\n102 <-> 94\n103 <-> 95\n106 <-> 98\n107 <-> 99\n110 <-> 102\n111 <-> 103\n114 <-> 106\n115 <-> 107\n118 <-> 110\n119 <-> 111\n123 <-> 114\n127 <-> 118\n131 <-> 122\n135 <-> 126\n139 <-> 130\n143 <-> 134\n147 <-> 138\n148 <-> 139\n149 <-> 140\n152 <-> 143\n153 <-> 144\n156 <-> 147\n157 <-> 148\n158 <-> 149\n159 <-> 150\n160 <-> 151\n161 <-> 152\n162 <-> 153\n163 <-> 154\n164 <-> 155\n165 <-> 156\n166 <-> 157\n167 <-> 158\n168 <-> 159\n169 <-> 160\n173 <-> 164\n174 <-> 165\n177 <-> 168\n178 <-> 169\n179 <-> 170\nNot mapped:\n48\n52\n"
  },
  {
    "path": "testData/results/TestPrivateEmptyConstructor.dec",
    "content": "package pkg;\n\npublic final class TestPrivateEmptyConstructor {\n   private TestPrivateEmptyConstructor() {\n   }// 4\n\n   public final void test() {\n      System.out.println(\"test\");// 7\n   }// 8\n}\n\nclass 'pkg/TestPrivateEmptyConstructor' {\n   method '<init> ()V' {\n      4      4\n   }\n\n   method 'test ()V' {\n      0      7\n      3      7\n      5      7\n      8      8\n   }\n}\n\nLines mapping:\n4 <-> 5\n7 <-> 8\n8 <-> 9\n"
  },
  {
    "path": "testData/results/TestRecordAnno.dec",
    "content": "package records;\n\npublic record TestRecordAnno(@RC @TA int x, int y) {\n   public TestRecordAnno(@TA int x, @P int y) {\n      this.x = x;\n      this.y = y;\n   }\n\n   public @TA int x() {\n      return this.x;\n   }\n\n   @M\n   public int y() {\n      return this.y;// 5\n   }\n}\n\nclass 'records/TestRecordAnno' {\n   method '<init> (II)V' {\n      6      4\n      b      5\n      e      6\n   }\n\n   method 'x ()I' {\n      1      9\n      4      9\n   }\n\n   method 'y ()I' {\n      1      14\n      4      14\n   }\n}\n\nLines mapping:\n5 <-> 15\n"
  },
  {
    "path": "testData/results/TestRecordEmpty.dec",
    "content": "package records;\n\npublic record TestRecordEmpty() {\n   public int hashCode() {\n      return 0;// 5\n   }\n}\n\nclass 'records/TestRecordEmpty' {\n   method 'hashCode ()I' {\n      0      4\n      1      4\n   }\n}\n\nLines mapping:\n5 <-> 5\n"
  },
  {
    "path": "testData/results/TestRecordGenericVararg.dec",
    "content": "package records;\n\npublic record TestRecordGenericVararg<T>(T first, T... other) {\n   @SafeVarargs\n   public TestRecordGenericVararg(T first, T... other) {\n      this.first = first;// 5\n      this.other = other;\n   }\n\n   public T first() {\n      return this.first;\n   }\n\n   public T[] other() {\n      return this.other;// 3\n   }\n}\n\nclass 'records/TestRecordGenericVararg' {\n   method '<init> (Ljava/lang/Object;[Ljava/lang/Object;)V' {\n      6      5\n      b      6\n      e      7\n   }\n\n   method 'first ()Ljava/lang/Object;' {\n      1      10\n      4      10\n   }\n\n   method 'other ()[Ljava/lang/Object;' {\n      1      14\n      4      14\n   }\n}\n\nLines mapping:\n3 <-> 15\n5 <-> 6\n"
  },
  {
    "path": "testData/results/TestRecordSimple.dec",
    "content": "package records;\n\npublic record TestRecordSimple(int x, int y) {\n   public TestRecordSimple(int x, int y) {\n      this.x = x;\n      this.y = y;\n   }\n\n   public int x() {\n      return this.x;\n   }\n\n   public int y() {\n      return this.y;// 3\n   }\n}\n\nclass 'records/TestRecordSimple' {\n   method '<init> (II)V' {\n      6      4\n      b      5\n      e      6\n   }\n\n   method 'x ()I' {\n      1      9\n      4      9\n   }\n\n   method 'y ()I' {\n      1      13\n      4      13\n   }\n}\n\nLines mapping:\n3 <-> 14\n"
  },
  {
    "path": "testData/results/TestRecordVararg.dec",
    "content": "package records;\n\npublic record TestRecordVararg(int x, int[]... y) {\n   public TestRecordVararg(int x, int[]... y) {\n      this.x = x;\n      this.y = y;\n   }\n\n   public int x() {\n      return this.x;\n   }\n\n   public int[][] y() {\n      return this.y;// 3\n   }\n}\n\nclass 'records/TestRecordVararg' {\n   method '<init> (I[[I)V' {\n      6      4\n      b      5\n      e      6\n   }\n\n   method 'x ()I' {\n      1      9\n      4      9\n   }\n\n   method 'y ()[[I' {\n      1      13\n      4      13\n   }\n}\n\nLines mapping:\n3 <-> 14\n"
  },
  {
    "path": "testData/results/TestShadowing.dec",
    "content": "package pkg;\n\nclass TestShadowing extends TestShadowingSuperClass {\n   ext.Shadow.B instanceOfB = new ext.Shadow.B();\n   java.util.Calendar.Builder calBuilder = new java.util.Calendar.Builder();\n}\n\n"
  },
  {
    "path": "testData/results/TestSimpleInstanceOfRecordPatternJavac.dec",
    "content": "package pkg;\n\npublic class TestSimpleInstanceOfRecordPatternJavac {\n   public static void main(String[] args) {\n   }// 6\n\n   public static void instanceOfTest1(Object o) {\n      if (o instanceof R(Object s1)) {// 9\n         System.out.println(s1);// 10\n      }\n\n      System.out.println(\"1\");// 12\n   }\n\n   public static void tryInstanceOfTest1(Object o) {\n      int a = 1;// 16\n\n      try {\n         if (o instanceof R(String var9)) {// 18\n            a += 34;// 19\n         }\n      } catch (Exception var8) {// 21\n         if (o instanceof R(String var4)) {// 22\n            a += 34;// 23\n         }\n      }\n\n   }// 26\n\n   public static void negativeInstanceOfTest1(Object o) {\n      if (o instanceof R(Object s1)) {// 29\n         if (s1.hashCode() == 1) {// 31\n            System.out.println(s1);// 32\n            System.out.println(\"1\");// 33\n         }\n\n      }\n   }// 30 35\n\n   public static void instanceOfTest2(Object o) {\n      if (o instanceof R(String s1)) {// 38\n         System.out.println(s1);// 39\n         if (s1.hashCode() == 1) {// 40\n            System.out.println(\"1\");// 41\n         }\n      }\n\n      System.out.println(\"5\");// 44\n   }\n\n   public static void instanceOfTest3(Object o) {\n      if (o instanceof R(String s1)) {// 48\n         if (s1.isEmpty()) {// 49\n            System.out.println(\"111\");// 50\n            System.out.println(\"111\");// 51\n            System.out.println(\"111\");// 52\n            System.out.println(\"111\");// 53\n         }\n      }\n\n      System.out.println(\"s222222222222\");// 56\n      System.out.println(\"s222222222222\");// 57\n   }\n\n   public static void instanceOfTest4(Object o) {\n      if (o.hashCode() == 1 && o instanceof R(String s1)) {// 61 62\n         if (o instanceof R(String var4)) {// 63\n            if (s1.isEmpty()) {// 64\n               System.out.println(\"111\");// 65\n               System.out.println(\"111\");// 66\n               System.out.println(\"111\");// 67\n               System.out.println(\"111\");// 68\n            }\n         }\n      }\n\n      System.out.println(\"s222222222222\");// 73\n      System.out.println(\"s222222222222\");// 74\n   }\n\n   public static void instanceOfTestDouble1(Object o, Object o2) {\n      if (o instanceof R(Object r)) {// 78\n         System.out.println(r);// 79\n      }\n\n      if (o2 instanceof R(Object r)) {// 82\n         System.out.println(r);// 83\n      }\n\n      System.out.println(\"s2222222\");// 85\n   }\n\n   public static void instanceOfTestDouble2(Object o, Object o2) {\n      if (o instanceof R(Object s1)) {// 89\n         System.out.println(s1);// 90\n      }\n\n      if (o2 instanceof R(String r)) {// 93\n         System.out.println(r);// 94\n      }\n\n      System.out.println(\"2222222\");// 96\n   }\n\n   public static void instanceOfTestDoubleNegate2(Object o, Object o2) {\n      if (o instanceof R(Object s1)) {// 100\n         System.out.println(s1);// 103\n         if (o2 instanceof R(String r)) {// 105\n            System.out.println(r);// 108\n            System.out.println(\"2222222\");// 110\n            return;\n         }\n\n      }\n   }// 101 106\n\n   static record R(Object o) {\n      R(Object o) {\n         this.o = o;\n      }\n\n      public Object o() {\n         return this.o;// 113\n      }\n   }\n}\n\nclass 'pkg/TestSimpleInstanceOfRecordPatternJavac' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n   }\n\n   method 'instanceOfTest1 (Ljava/lang/Object;)V' {\n      1      7\n      4      7\n      13      8\n      17      8\n      1a      11\n      1d      11\n      1f      11\n      22      12\n   }\n\n   method 'tryInstanceOfTest1 (Ljava/lang/Object;)V' {\n      0      15\n      1      15\n      3      18\n      6      18\n      22      19\n      39      21\n      3b      22\n      3e      22\n      5b      23\n      6f      27\n   }\n\n   method 'negativeInstanceOfTest1 (Ljava/lang/Object;)V' {\n      1      30\n      4      30\n      16      37\n      17      31\n      18      31\n      1b      31\n      1f      32\n      23      32\n      26      33\n      29      33\n      2b      33\n      3f      37\n   }\n\n   method 'instanceOfTest2 (Ljava/lang/Object;)V' {\n      1      40\n      4      40\n      1d      41\n      21      41\n      25      42\n      28      42\n      29      42\n      2c      43\n      2f      43\n      31      43\n      34      47\n      37      47\n      39      47\n      3c      48\n   }\n\n   method 'instanceOfTest3 (Ljava/lang/Object;)V' {\n      1      51\n      4      51\n      1e      52\n      21      52\n      24      53\n      27      53\n      29      53\n      2c      54\n      2f      54\n      31      54\n      34      55\n      37      55\n      39      55\n      3c      56\n      3f      56\n      41      56\n      44      60\n      47      60\n      49      60\n      4c      61\n      4f      61\n      51      61\n      54      62\n   }\n\n   method 'instanceOfTest4 (Ljava/lang/Object;)V' {\n      1      65\n      4      65\n      5      65\n      9      65\n      c      65\n      26      66\n      29      66\n      47      67\n      4a      67\n      4d      68\n      50      68\n      52      68\n      55      69\n      58      69\n      5a      69\n      5d      70\n      60      70\n      62      70\n      65      71\n      68      71\n      6a      71\n      6d      76\n      70      76\n      72      76\n      75      77\n      78      77\n      7a      77\n      7d      78\n   }\n\n   method 'instanceOfTestDouble1 (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      1      81\n      4      81\n      15      82\n      19      82\n      1d      85\n      20      85\n      31      86\n      35      86\n      38      89\n      3b      89\n      3d      89\n      40      90\n   }\n\n   method 'instanceOfTestDouble2 (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      1      93\n      4      93\n      15      94\n      19      94\n      1d      97\n      20      97\n      3c      98\n      40      98\n      43      101\n      46      101\n      48      101\n      4b      102\n   }\n\n   method 'instanceOfTestDoubleNegate2 (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      1      105\n      4      105\n      15      106\n      18      114\n      1c      106\n      20      107\n      21      107\n      42      108\n      45      114\n      49      108\n      4a      109\n      4d      109\n      50      109\n      52      110\n   }\n}\n\nclass 'pkg/TestSimpleInstanceOfRecordPatternJavac$R' {\n   method '<init> (Ljava/lang/Object;)V' {\n      6      118\n      9      119\n   }\n\n   method 'o ()Ljava/lang/Object;' {\n      1      122\n      4      122\n   }\n}\n\nLines mapping:\n6 <-> 5\n9 <-> 8\n10 <-> 9\n12 <-> 12\n16 <-> 16\n18 <-> 19\n19 <-> 20\n21 <-> 22\n22 <-> 23\n23 <-> 24\n26 <-> 28\n29 <-> 31\n30 <-> 38\n31 <-> 32\n32 <-> 33\n33 <-> 34\n35 <-> 38\n38 <-> 41\n39 <-> 42\n40 <-> 43\n41 <-> 44\n44 <-> 48\n48 <-> 52\n49 <-> 53\n50 <-> 54\n51 <-> 55\n52 <-> 56\n53 <-> 57\n56 <-> 61\n57 <-> 62\n61 <-> 66\n62 <-> 66\n63 <-> 67\n64 <-> 68\n65 <-> 69\n66 <-> 70\n67 <-> 71\n68 <-> 72\n73 <-> 77\n74 <-> 78\n78 <-> 82\n79 <-> 83\n82 <-> 86\n83 <-> 87\n85 <-> 90\n89 <-> 94\n90 <-> 95\n93 <-> 98\n94 <-> 99\n96 <-> 102\n100 <-> 106\n101 <-> 115\n103 <-> 107\n105 <-> 108\n106 <-> 115\n108 <-> 109\n110 <-> 110\n113 <-> 123\nNot mapped:\n13\n25\n45\n58\n75\n86\n97\n111\n"
  },
  {
    "path": "testData/results/TestStaticNameClash.dec",
    "content": "package pkg;\n\npublic class TestStaticNameClash {\n   public static String property;\n\n   public static void setProperty(String property) {\n      TestStaticNameClash.property = property;// 8\n   }// 9\n}\n\nclass 'pkg/TestStaticNameClash' {\n   method 'setProperty (Ljava/lang/String;)V' {\n      1      6\n      4      7\n   }\n}\n\nLines mapping:\n8 <-> 7\n9 <-> 8\n"
  },
  {
    "path": "testData/results/TestStringConcat.dec",
    "content": "package pkg;\n\npublic class TestStringConcat {\n   public String test1(String var1, int var2) {\n      return var1 + var2;// 20\n   }\n\n   public String test2(String var1, int var2, Object var3) {\n      return \"(\" + var1 + \"-\" + var2 + \"---\" + var3 + \")\";// 24\n   }\n}\n\nclass 'pkg/TestStringConcat' {\n   method 'test1 (Ljava/lang/String;I)Ljava/lang/String;' {\n      f      4\n      12      4\n   }\n\n   method 'test2 (Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String;' {\n      7      8\n      10      8\n      19      8\n      22      8\n      27      8\n      2a      8\n   }\n}\n\nLines mapping:\n20 <-> 5\n24 <-> 9\n"
  },
  {
    "path": "testData/results/TestStringLiterals.dec",
    "content": "package pkg;\n\npublic class TestStringLiterals {\n   public static void main(String[] var0) {\n      String var1 = \"abc\\b\\t\\n\\f\\r\\\"'\\\\def\";// 20\n      char[] var2 = new char[]{'\\b', '\\t', '\\n', '\\f', '\\r', '\"', '\\'', '\\\\'};// 21\n      System.out.println(var1);// 22\n      System.out.println(var2);// 23\n   }// 24\n}\n\nclass 'pkg/TestStringLiterals' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n      2      4\n      9      5\n      e      5\n      13      5\n      18      5\n      1d      5\n      22      5\n      28      5\n      2e      5\n      31      5\n      32      6\n      36      6\n      39      7\n      3d      7\n      40      8\n   }\n}\n\nLines mapping:\n20 <-> 5\n21 <-> 6\n22 <-> 7\n23 <-> 8\n24 <-> 9\n"
  },
  {
    "path": "testData/results/TestSuperInner.dec",
    "content": "package pkg;\n\nclass TestSuperInner extends TestSuperInnerBase {\n   protected abstract class Inner2 extends TestSuperInnerBase.Inner {\n      protected Inner2() {\n         super();// 4\n      }\n   }\n}\n\nclass 'pkg/TestSuperInner$Inner2' {\n   method '<init> (Lpkg/TestSuperInner;)V' {\n      7      5\n      a      6\n   }\n}\n\nLines mapping:\n4 <-> 6\n"
  },
  {
    "path": "testData/results/TestSuspendLambdaKt.dec",
    "content": "package pkg;\n\nimport kotlin.Metadata;\nimport kotlin.Result;\nimport kotlin.Unit;\nimport kotlin.coroutines.Continuation;\nimport kotlin.coroutines.intrinsics.IntrinsicsKt;\nimport kotlin.coroutines.jvm.internal.DebugMetadata;\nimport kotlin.coroutines.jvm.internal.SuspendLambda;\nimport kotlin.jvm.functions.Function1;\nimport kotlin.jvm.internal.Intrinsics;\nimport org.jetbrains.annotations.NotNull;\nimport org.jetbrains.annotations.Nullable;\n\n@Metadata(\n   mv = {1, 1, 13},\n   bv = {1, 0, 3},\n   k = 2,\n   d1 = {\"\\u0000\\u0016\\n\\u0000\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0003\\\",\\u0010\\u0000\\u001a\\u0018\\b\\u0001\\u0012\\n\\u0012\\b\\u0012\\u0004\\u0012\\u00020\\u00030\\u0002\\u0012\\u0006\\u0012\\u0004\\u0018\\u00010\\u00040\\u0001ø\\u0001\\u0000¢\\u0006\\n\\n\\u0002\\u0010\\u0007\\u001a\\u0004\\b\\u0005\\u0010\\u0006\\u0082\\u0002\\u0004\\n\\u0002\\b\\u0019\"},\n   d2 = {\"sl1\", \"Lkotlin/Function1;\", \"Lkotlin/coroutines/Continuation;\", \"\", \"\", \"getSl1\", \"()Lkotlin/jvm/functions/Function1;\", \"Lkotlin/jvm/functions/Function1;\"}\n)\npublic final class TestSuspendLambdaKt {\n   @NotNull\n   private static final Function1<Continuation<? super Unit>, Object> sl1;\n\n   @NotNull\n   public static final Function1<Continuation<? super Unit>, Object> getSl1() {\n      return sl1;\n   }\n\n   static {\n      @DebugMetadata(\n         f = \"TestSuspendLambda.kt\",\n         l = {3},\n         i = {},\n         s = {},\n         n = {},\n         m = \"invokeSuspend\",\n         c = \"pkg/TestSuspendLambdaKt$sl1$1\"\n      )\n      @Metadata(\n         mv = {1, 1, 13},\n         bv = {1, 0, 3},\n         k = 3,\n         d1 = {\"\\u0000\\n\\n\\u0000\\n\\u0002\\u0010\\u0002\\n\\u0002\\b\\u0002\\u0010\\u0000\\u001a\\u00020\\u0001H\\u008a@ø\\u0001\\u0000¢\\u0006\\u0004\\b\\u0002\\u0010\\u0003\"},\n         d2 = {\"<anonymous>\", \"\", \"invoke\", \"(Ljava/lang/Object;)Ljava/lang/Object;\"}\n      )\n      final class NamelessClass_1 extends SuspendLambda implements Function1<Continuation<? super Unit>, Object> {\n         int label;\n\n         @Nullable\n         public final Object invokeSuspend(@NotNull Object result) {\n            Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();\n            switch (this.label) {\n               case 0:\n                  if (result instanceof Result.Failure) {\n                     throw ((Result.Failure)result).exception;\n                  }\n\n                  String var2 = \"SL1\";// 4\n                  System.out.println(var2);\n                  return Unit.INSTANCE;// 5\n               default:\n                  throw new IllegalStateException(\"call to 'resume' before 'invoke' with coroutine\");\n            }\n         }\n\n         NamelessClass_1(Continuation var1) {\n            super(1, var1);\n         }\n\n         @NotNull\n         public final Continuation<Unit> create(@NotNull Continuation<?> completion) {\n            Intrinsics.checkParameterIsNotNull(completion, \"completion\");\n            NamelessClass_1 var2 = new NamelessClass_1(completion);\n            return var2;\n         }\n\n         public final Object invoke(Object var1) {\n            return ((NamelessClass_1)this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);\n         }\n      }\n\n      sl1 = (Function1)(new NamelessClass_1((Continuation)null));// 3\n   }\n}\n\nclass 'pkg/TestSuspendLambdaKt' {\n   method 'getSl1 ()Lkotlin/jvm/functions/Function1;' {\n      0      27\n      3      27\n   }\n\n   method '<clinit> ()V' {\n      4      83\n      8      83\n      b      83\n      e      84\n   }\n}\n\nclass 'pkg/TestSuspendLambdaKt$sl1$1' {\n   method 'invokeSuspend (Ljava/lang/Object;)Ljava/lang/Object;' {\n      0      52\n      3      52\n      5      53\n      8      53\n      1e      55\n      21      55\n      24      56\n      27      56\n      2a      56\n      2c      59\n      2e      59\n      2f      60\n      33      60\n      36      61\n      39      61\n      3e      63\n      43      63\n   }\n\n   method '<init> (Lkotlin/coroutines/Continuation;)V' {\n      1      68\n      3      68\n      6      69\n   }\n\n   method 'create (Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation;' {\n      1      73\n      3      73\n      e      74\n      10      75\n   }\n\n   method 'invoke (Ljava/lang/Object;)Ljava/lang/Object;' {\n      2      79\n      5      79\n      8      79\n      b      79\n      e      79\n      11      79\n   }\n}\n\nLines mapping:\n3 <-> 84\n4 <-> 60\n5 <-> 62\n"
  },
  {
    "path": "testData/results/TestSwitchClassReferencesEcj.dec",
    "content": "package pkg;\n\npublic class TestSwitchClassReferencesEcj {\n   public static void testObject(Object o) {\n      switch (o) {// 6\n         case String var3 -> System.out.println(\"s\");// 7\n         case Integer var4 -> System.out.println(\"i\");// 8\n         default -> System.out.println(o);// 9\n      }\n\n   }// 11\n\n   public static void testObject2(Object o) {\n      switch (o) {// 14\n         case null -> System.out.println(\"null\");// 18\n         case String var3 -> System.out.println(\"s\");// 15\n         case Integer var4 -> System.out.println(\"i\");// 16\n         default -> System.out.println(o);// 17\n      }\n\n   }// 20\n\n   public static void testObject3(Object o) {\n      switch (o) {// 23\n         case null -> System.out.println(\"null\");// 26\n         case String var3 -> System.out.println(\"s\");// 24\n         case Integer var4 -> System.out.println(\"i\");// 25\n         default -> System.out.println(\"o\");// 27\n      }\n\n   }// 29\n\n   public static void testObject4(Object o) {\n      switch (o) {// 32\n         case String var3 -> System.out.println(\"s\");// 33\n         case Integer var4 -> System.out.println(\"i\");// 34\n         default -> System.out.println(\"o\");// 35\n      }\n\n   }// 37\n}\n\nclass 'pkg/TestSwitchClassReferencesEcj' {\n   method 'testObject (Ljava/lang/Object;)V' {\n      10      4\n      31      5\n      34      5\n      36      5\n      42      6\n      45      6\n      47      6\n      50      7\n      54      7\n      57      10\n   }\n\n   method 'testObject2 (Ljava/lang/Object;)V' {\n      b      13\n      2d      15\n      30      15\n      32      15\n      3e      16\n      41      16\n      43      16\n      4c      17\n      50      17\n      56      14\n      59      14\n      5b      14\n      5e      20\n   }\n\n   method 'testObject3 (Ljava/lang/Object;)V' {\n      b      23\n      29      25\n      2c      25\n      2e      25\n      3a      26\n      3d      26\n      3f      26\n      45      24\n      48      24\n      4a      24\n      50      27\n      53      27\n      55      27\n      58      30\n   }\n\n   method 'testObject4 (Ljava/lang/Object;)V' {\n      10      33\n      2d      34\n      30      34\n      32      34\n      3e      35\n      41      35\n      43      35\n      49      36\n      4c      36\n      4e      36\n      51      39\n   }\n}\n\nLines mapping:\n6 <-> 5\n7 <-> 6\n8 <-> 7\n9 <-> 8\n11 <-> 11\n14 <-> 14\n15 <-> 16\n16 <-> 17\n17 <-> 18\n18 <-> 15\n20 <-> 21\n23 <-> 24\n24 <-> 26\n25 <-> 27\n26 <-> 25\n27 <-> 28\n29 <-> 31\n32 <-> 34\n33 <-> 35\n34 <-> 36\n35 <-> 37\n37 <-> 40\n"
  },
  {
    "path": "testData/results/TestSwitchClassReferencesFastExitEcj.dec",
    "content": "package pkg;\n\npublic class TestSwitchClassReferencesFastExitEcj {\n   public static void testObject(Object o) {\n      label16:\n      while(true) {\n         int i = 0;\n\n         while(i < o.hashCode()) {\n            switch (o) {// 8\n               case String var4:\n                  System.out.println(\"s\");// 10\n                  System.exit(0);// 11\n                  ++i;// 12\n                  break;// 7\n               case Integer var5:\n                  System.out.println(\"ii\");// 14\n                  continue label16;// 15\n               default:\n                  System.out.println(\"s\");// 17\n                  return;// 18\n            }\n         }\n      }\n   }\n\n   public static void testObject2(Object o) {\n      label16:\n      while(true) {\n         int i = 0;\n\n         while(i < o.hashCode()) {\n            switch (o) {// 28\n               case String var4:\n                  System.out.println(\"s\");// 30\n                  System.exit(0);// 31\n                  ++i;// 32\n                  break;// 27\n               case Integer var5:\n                  System.out.println(\"ii\");// 35\n                  continue label16;// 36\n               default:\n                  System.out.println(\"s\");// 39\n                  return;// 40\n            }\n         }\n      }\n   }\n}\n\nclass 'pkg/TestSwitchClassReferencesFastExitEcj' {\n   method 'testObject (Ljava/lang/Object;)V' {\n      0      6\n      1      6\n      15      9\n      36      11\n      39      11\n      3b      11\n      3e      12\n      3f      12\n      42      13\n      4b      16\n      4e      16\n      50      16\n      53      17\n      59      19\n      5c      19\n      5e      19\n      61      20\n      64      14\n      69      8\n      6c      8\n   }\n\n   method 'testObject2 (Ljava/lang/Object;)V' {\n      0      29\n      1      29\n      15      32\n      36      34\n      39      34\n      3b      34\n      3e      35\n      3f      35\n      42      36\n      4b      39\n      4e      39\n      50      39\n      53      40\n      59      42\n      5c      42\n      5e      42\n      61      43\n      64      37\n      69      31\n      6c      31\n   }\n}\n\nLines mapping:\n7 <-> 15\n8 <-> 10\n10 <-> 12\n11 <-> 13\n12 <-> 14\n14 <-> 17\n15 <-> 18\n17 <-> 20\n18 <-> 21\n27 <-> 38\n28 <-> 33\n30 <-> 35\n31 <-> 36\n32 <-> 37\n35 <-> 40\n36 <-> 41\n39 <-> 43\n40 <-> 44\nNot mapped:\n6\n9\n13\n16\n22\n26\n29\n34\n38\n45\n"
  },
  {
    "path": "testData/results/TestSwitchClassReferencesFastExitJavac.dec",
    "content": "package pkg;\n\npublic class TestSwitchClassReferencesFastExitJavac {\n   public static void testObject(Object o) {\n      label15:\n      while(true) {\n         int i = 0;// 8\n\n         while(i < o.hashCode()) {\n            switch (o) {// 9\n               case String var4:\n                  System.out.println(\"s\");// 11\n                  System.exit(0);// 12\n                  ++i;// 13\n                  break;\n               case Integer var5:\n                  System.out.println(\"ii\");// 15\n                  continue label15;// 16\n               default:\n                  System.out.println(\"s\");// 18\n                  return;// 19\n            }\n         }\n      }\n   }\n\n   public static void testObject2(Object o) {\n      label15:\n      while(true) {\n         int i = 0;// 28\n\n         while(i < o.hashCode()) {\n            switch (o) {// 29\n               case String var4:\n                  System.out.println(\"s\");// 31\n                  System.exit(0);// 32\n                  ++i;// 33\n                  break;\n               case Integer var5:\n                  System.out.println(\"ii\");// 36\n                  continue label15;// 37\n               default:\n                  System.out.println(\"s\");// 40\n                  return;// 41\n            }\n         }\n      }\n   }\n}\n\nclass 'pkg/TestSwitchClassReferencesFastExitJavac' {\n   method 'testObject (Ljava/lang/Object;)V' {\n      0      6\n      1      6\n      4      8\n      7      8\n      1a      9\n      3a      11\n      3d      11\n      3f      11\n      42      12\n      43      12\n      46      13\n      4f      16\n      52      16\n      54      16\n      57      17\n      5d      19\n      60      19\n      62      19\n      65      20\n      6b      14\n   }\n\n   method 'testObject2 (Ljava/lang/Object;)V' {\n      0      29\n      1      29\n      4      31\n      7      31\n      1a      32\n      3a      34\n      3d      34\n      3f      34\n      42      35\n      43      35\n      46      36\n      4f      39\n      52      39\n      54      39\n      57      40\n      5d      42\n      60      42\n      62      42\n      65      43\n      6b      37\n   }\n}\n\nLines mapping:\n8 <-> 7\n9 <-> 10\n11 <-> 12\n12 <-> 13\n13 <-> 14\n15 <-> 17\n16 <-> 18\n18 <-> 20\n19 <-> 21\n28 <-> 30\n29 <-> 33\n31 <-> 35\n32 <-> 36\n33 <-> 37\n36 <-> 40\n37 <-> 41\n40 <-> 43\n41 <-> 44\nNot mapped:\n10\n14\n17\n23\n30\n35\n39\n46\n"
  },
  {
    "path": "testData/results/TestSwitchClassReferencesJavac.dec",
    "content": "package pkg;\n\npublic class TestSwitchClassReferencesJavac {\n   public static void testObject(Object o) {\n      switch (o) {// 6\n         case String var3 -> System.out.println(\"s\");// 7\n         case Integer var4 -> System.out.println(\"i\");// 8\n         default -> System.out.println(o);// 9\n      }\n\n   }// 11\n\n   public static void testObject2(Object o) {\n      switch (o) {// 14\n         case null -> System.out.println(\"null\");// 18\n         case String var3 -> System.out.println(\"s\");// 15\n         case Integer var4 -> System.out.println(\"i\");// 16\n         case Object var6 -> System.out.println(o);// 17\n      }\n\n   }// 20\n\n   public static void testObject3(Object o) {\n      switch (o) {// 23\n         case null -> System.out.println(\"null\");// 26\n         case String var3 -> System.out.println(\"s\");// 24\n         case Integer var4 -> System.out.println(\"i\");// 25\n         default -> System.out.println(\"o\");// 27\n      }\n\n   }// 29\n\n   public static void testObject4(Object o) {\n      switch (o) {// 32\n         case String var3 -> System.out.println(\"s\");// 33\n         case Integer var4 -> System.out.println(\"i\");// 34\n         default -> System.out.println(\"o\");// 35\n      }\n\n   }// 37\n}\n\nclass 'pkg/TestSwitchClassReferencesJavac' {\n   method 'testObject (Ljava/lang/Object;)V' {\n      10      4\n      31      5\n      34      5\n      36      5\n      42      6\n      45      6\n      47      6\n      50      7\n      54      7\n      57      10\n   }\n\n   method 'testObject2 (Ljava/lang/Object;)V' {\n      b      13\n      37      15\n      3a      15\n      3c      15\n      48      16\n      4b      16\n      4d      16\n      56      17\n      5a      17\n      60      14\n      63      14\n      65      14\n      68      20\n   }\n\n   method 'testObject3 (Ljava/lang/Object;)V' {\n      b      23\n      29      25\n      2c      25\n      2e      25\n      3a      26\n      3d      26\n      3f      26\n      45      24\n      48      24\n      4a      24\n      50      27\n      53      27\n      55      27\n      58      30\n   }\n\n   method 'testObject4 (Ljava/lang/Object;)V' {\n      10      33\n      31      34\n      34      34\n      36      34\n      42      35\n      45      35\n      47      35\n      4d      36\n      50      36\n      52      36\n      55      39\n   }\n}\n\nLines mapping:\n6 <-> 5\n7 <-> 6\n8 <-> 7\n9 <-> 8\n11 <-> 11\n14 <-> 14\n15 <-> 16\n16 <-> 17\n17 <-> 18\n18 <-> 15\n20 <-> 21\n23 <-> 24\n24 <-> 26\n25 <-> 27\n26 <-> 25\n27 <-> 28\n29 <-> 31\n32 <-> 34\n33 <-> 35\n34 <-> 36\n35 <-> 37\n37 <-> 40\n"
  },
  {
    "path": "testData/results/TestSwitchGuarded2Javac.dec",
    "content": "package pkg;\n\npublic class TestSwitchGuarded2Javac {\n   public static void main(String[] args) {\n      testObject0(4);// 6\n   }// 7\n\n   public static void testObject0(Object o) {\n      try {\n         System.out.println(\"inside try 2\");// 11\n\n         switch (o) {// 12\n            case Integer var3 when var3 > 3:// 13\n               System.out.println(\"4\");// 14\n               throw new RuntimeException();// 15\n            default:\n               System.out.println(o);// 17\n               System.out.println(\"2\");// 19\n               return;// 25\n         }\n      } catch (UnsupportedOperationException var8) {// 20\n         System.out.println(\"exception\");// 21\n      } finally {\n         System.out.println(\"finally\");// 23\n      }\n\n   }\n\n   public static void testObject1(Object o) {\n      System.out.println(\"2\");// 28\n\n      switch (o) {// 29\n         case Integer n when !(n <= 1):// 30\n            if (n == 1) {// 31\n               System.out.println(\"212\");// 32\n            }\n\n            System.out.println(2);// 34\n            System.out.println(1);// 35\n            break;// 36\n         case Integer n when !(n <= 2):// 37\n            if (n == 1) {// 38\n               System.out.println(\"4\");// 39\n            }\n            break;\n         case Integer var5 when !(var5 <= 3):// 42\n            System.out.println(\"4\");// 43\n            break;// 44\n         default:\n            System.out.println(o);// 45\n            break;\n      }\n\n      System.out.println(\"2\");// 47\n      System.out.println(\"2\");// 48\n\n      switch (o) {// 49\n         case Integer n when !(n <= 1):// 50\n            if (n == 1) {// 51\n               System.out.println(\"212\");// 52\n            }\n\n            System.out.println(2);// 54\n            System.out.println(1);// 55\n            break;// 56\n         case Integer n when !(n <= 2):// 57\n            if (n == 1) {// 58\n               System.out.println(\"4\");// 59\n            }\n            break;\n         case Integer var5 when !(var5 <= 3):// 62\n            System.out.println(\"4\");// 63\n            break;// 64\n         default:\n            System.out.println(o);// 65\n            break;\n      }\n\n      System.out.println(\"2\");// 67\n   }// 68\n\n   public static void testObject2(Object o) {\n      switch (o) {// 72\n         case Integer n:\n            if (n == 1) {// 74\n               System.out.println(\"2\");// 75\n            }\n\n            System.out.println(1);// 77\n            System.out.println(1);// 78\n            break;// 79\n         default:\n            System.out.println(o);// 80\n      }\n\n      System.out.println(\"2\");// 82\n      switch (o) {// 83\n         case Integer n:\n            if (n == 1) {// 85\n               System.out.println(\"1\");// 86\n            }\n\n            System.out.println(1);// 88\n            System.out.println(1);// 89\n            break;// 90\n         default:\n            System.out.println(o);// 91\n      }\n\n      System.out.println(\"2\");// 93\n   }// 94\n\n   public static void testObject3(Object o) {\n      System.out.println(\"2\");// 97\n\n      switch (o) {// 98\n         case Integer n when !(n <= 1):// 99\n            if (n == 1) {// 100\n               System.out.println(\"212\");// 101\n            }\n\n            System.out.println(2);// 103\n            System.out.println(1);// 104\n            break;// 105\n         case Integer n when !(n <= 2):// 106\n            if (n == 1) {// 107\n               System.out.println(\"4\");// 108\n            }\n            break;\n         case Integer var5 when !(var5 <= 3):// 111\n            System.out.println(\"4\");// 112\n            break;// 113\n         default:\n            System.out.println(o);// 114\n            break;\n      }\n\n      System.out.println(\"2\");// 116\n   }// 117\n}\n\nclass 'pkg/TestSwitchGuarded2Javac' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n      4      4\n      7      5\n   }\n\n   method 'testObject0 (Ljava/lang/Object;)V' {\n      0      9\n      3      9\n      5      9\n      18      11\n      32      12\n      35      12\n      36      12\n      3e      13\n      41      13\n      43      13\n      4d      14\n      51      16\n      55      16\n      58      17\n      5b      17\n      5e      17\n      6e      20\n      6f      21\n      72      21\n      74      21\n      82      23\n      84      23\n      87      23\n      8f      18\n   }\n\n   method 'testObject1 (Ljava/lang/Object;)V' {\n      0      29\n      3      29\n      5      29\n      18      31\n      3a      32\n      3d      32\n      3e      32\n      47      33\n      4a      33\n      4b      33\n      4e      34\n      51      34\n      53      34\n      56      37\n      59      37\n      5a      37\n      5d      38\n      60      38\n      61      38\n      64      39\n      6f      40\n      72      40\n      73      40\n      7d      41\n      80      41\n      81      41\n      84      42\n      87      42\n      89      42\n      97      45\n      9a      45\n      9b      45\n      a3      46\n      a6      46\n      a8      46\n      ab      47\n      b1      49\n      b5      49\n      b8      50\n      bb      53\n      be      53\n      c0      53\n      c3      54\n      c6      54\n      c8      54\n      db      56\n      fa      57\n      fd      57\n      fe      57\n      107      58\n      10a      58\n      10b      58\n      10e      59\n      111      59\n      113      59\n      116      62\n      119      62\n      11a      62\n      11d      63\n      120      63\n      121      63\n      124      64\n      12f      65\n      132      65\n      133      65\n      13d      66\n      140      66\n      141      66\n      144      67\n      147      67\n      149      67\n      157      70\n      15a      70\n      15b      70\n      163      71\n      166      71\n      168      71\n      16b      72\n      171      74\n      175      74\n      178      75\n      17b      78\n      17e      78\n      180      78\n      183      79\n   }\n\n   method 'testObject2 (Ljava/lang/Object;)V' {\n      10      82\n      2a      84\n      2d      84\n      2e      84\n      31      85\n      34      85\n      36      85\n      39      88\n      3c      88\n      3d      88\n      40      89\n      43      89\n      44      89\n      47      90\n      4d      92\n      51      92\n      54      95\n      57      95\n      59      95\n      6c      96\n      86      98\n      89      98\n      8a      98\n      8d      99\n      90      99\n      92      99\n      95      102\n      98      102\n      99      102\n      9c      103\n      9f      103\n      a0      103\n      a3      104\n      a9      106\n      ad      106\n      b0      109\n      b3      109\n      b5      109\n      b8      110\n   }\n\n   method 'testObject3 (Ljava/lang/Object;)V' {\n      0      113\n      3      113\n      5      113\n      18      115\n      3a      116\n      3d      116\n      3e      116\n      47      117\n      4a      117\n      4b      117\n      4e      118\n      51      118\n      53      118\n      56      121\n      59      121\n      5a      121\n      5d      122\n      60      122\n      61      122\n      64      123\n      6f      124\n      72      124\n      73      124\n      7d      125\n      80      125\n      81      125\n      84      126\n      87      126\n      89      126\n      97      129\n      9a      129\n      9b      129\n      a3      130\n      a6      130\n      a8      130\n      ab      131\n      b1      133\n      b5      133\n      b8      134\n      bb      137\n      be      137\n      c0      137\n      c3      138\n   }\n}\n\nLines mapping:\n6 <-> 5\n7 <-> 6\n11 <-> 10\n12 <-> 12\n13 <-> 13\n14 <-> 14\n15 <-> 15\n17 <-> 17\n19 <-> 18\n20 <-> 21\n21 <-> 22\n23 <-> 24\n25 <-> 19\n28 <-> 30\n29 <-> 32\n30 <-> 33\n31 <-> 34\n32 <-> 35\n34 <-> 38\n35 <-> 39\n36 <-> 40\n37 <-> 41\n38 <-> 42\n39 <-> 43\n42 <-> 46\n43 <-> 47\n44 <-> 48\n45 <-> 50\n47 <-> 54\n48 <-> 55\n49 <-> 57\n50 <-> 58\n51 <-> 59\n52 <-> 60\n54 <-> 63\n55 <-> 64\n56 <-> 65\n57 <-> 66\n58 <-> 67\n59 <-> 68\n62 <-> 71\n63 <-> 72\n64 <-> 73\n65 <-> 75\n67 <-> 79\n68 <-> 80\n72 <-> 83\n74 <-> 85\n75 <-> 86\n77 <-> 89\n78 <-> 90\n79 <-> 91\n80 <-> 93\n82 <-> 96\n83 <-> 97\n85 <-> 99\n86 <-> 100\n88 <-> 103\n89 <-> 104\n90 <-> 105\n91 <-> 107\n93 <-> 110\n94 <-> 111\n97 <-> 114\n98 <-> 116\n99 <-> 117\n100 <-> 118\n101 <-> 119\n103 <-> 122\n104 <-> 123\n105 <-> 124\n106 <-> 125\n107 <-> 126\n108 <-> 127\n111 <-> 130\n112 <-> 131\n113 <-> 132\n114 <-> 134\n116 <-> 138\n117 <-> 139\nNot mapped:\n24\n73\n84\n"
  },
  {
    "path": "testData/results/TestSwitchGuardedEcj.dec",
    "content": "package pkg;\n\npublic class TestSwitchGuardedEcj {\n   public static void main(String[] args) {\n   }// 7\n\n   public static void testObject(Object o) {\n      switch (o) {// 10\n         case String var3 when var3.isEmpty() && var3.getBytes().length == 2 -> System.out.println(\"empty s\");// 11\n         case String var4 -> System.out.println(\"s\");// 12\n         case Integer var5 -> System.out.println(\"i\");// 13\n         default -> System.out.println(o);// 14\n      }\n\n      System.out.println(\"1\");// 16\n   }// 17\n\n   public static void testObject2(Object o) {\n      switch (o) {// 20\n         case String var3 when var3.isEmpty() && var3.getBytes().length == 2:// 21\n            System.out.println(\"empty s\");\n            return;// 26\n         case String var4:\n            System.out.println(\"s\");// 22\n            return;\n         case Integer var5:\n            System.out.println(\"ii\");// 23\n            return;\n         default:\n            System.out.println(o);// 24\n            return;\n      }\n   }\n\n   public static void testObject3(Object o) {\n      label33:\n      while(true) {\n         if (o.hashCode() == 1) {// 30\n            switch (o) {// 31\n               case String var3 when var3.isEmpty() && var3.getBytes().length == 2:// 32\n                  System.out.println(\"empty s\");// 33\n                  break;// 34\n               case String var4:\n                  System.out.println(\"s\");// 36\n                  continue label33;// 37\n               case Integer var5:\n                  System.out.println(\"i\");// 39\n                  break;// 40\n               default:\n                  System.out.println(o);// 42\n                  break;// 45\n            }\n         }\n\n         System.out.println(\"1\");// 47\n         return;// 48\n      }\n   }\n}\n\nclass 'pkg/TestSwitchGuardedEcj' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n   }\n\n   method 'testObject (Ljava/lang/Object;)V' {\n      10      7\n      36      8\n      3d      8\n      40      8\n      41      8\n      42      8\n      4a      8\n      4d      8\n      4f      8\n      5b      9\n      5e      9\n      60      9\n      6c      10\n      6f      10\n      71      10\n      7a      11\n      7e      11\n      81      14\n      84      14\n      86      14\n      89      15\n   }\n\n   method 'testObject2 (Ljava/lang/Object;)V' {\n      10      18\n      36      19\n      3d      19\n      40      19\n      41      19\n      42      19\n      4a      20\n      4d      20\n      4f      20\n      5b      23\n      5e      23\n      60      23\n      6c      26\n      6f      26\n      71      26\n      7a      29\n      7e      29\n      81      21\n   }\n\n   method 'testObject3 (Ljava/lang/Object;)V' {\n      13      38\n      36      39\n      3d      39\n      40      39\n      41      39\n      42      39\n      4a      40\n      4d      40\n      4f      40\n      52      41\n      5b      43\n      5e      43\n      60      43\n      63      44\n      6c      46\n      6f      46\n      71      46\n      74      47\n      7a      49\n      7e      49\n      81      50\n      85      37\n      88      37\n      89      37\n      8c      54\n      8f      54\n      91      54\n      94      55\n   }\n}\n\nLines mapping:\n7 <-> 5\n10 <-> 8\n11 <-> 9\n12 <-> 10\n13 <-> 11\n14 <-> 12\n16 <-> 15\n17 <-> 16\n20 <-> 19\n21 <-> 20\n22 <-> 24\n23 <-> 27\n24 <-> 30\n26 <-> 22\n30 <-> 38\n31 <-> 39\n32 <-> 40\n33 <-> 41\n34 <-> 42\n36 <-> 44\n37 <-> 45\n39 <-> 47\n40 <-> 48\n42 <-> 50\n45 <-> 51\n47 <-> 55\n48 <-> 56\nNot mapped:\n35\n38\n41\n"
  },
  {
    "path": "testData/results/TestSwitchGuardedJavac.dec",
    "content": "package pkg;\n\npublic class TestSwitchGuardedJavac {\n   public static void main(String[] args) {\n   }// 7\n\n   public static void testObject(Object o) {\n      switch (o) {// 10\n         case String var3 when var3.isEmpty() && var3.getBytes().length == 2 -> System.out.println(\"empty s\");// 11\n         case String var4 -> System.out.println(\"s\");// 12\n         case Integer var5 -> System.out.println(\"iii\");// 13\n         default -> System.out.println(o);// 14\n      }\n\n      System.out.println(\"1\");// 16\n   }// 17\n\n   public static void testObject2(Object o) {\n      switch (o) {// 20\n         case String var3 when var3.isEmpty() && var3.getBytes().length == 2:\n            System.out.println(\"empty s\");// 21\n            return;// 26\n         case String var4:\n            System.out.println(\"s\");// 22\n            return;\n         case Integer var5:\n            System.out.println(\"ii\");// 23\n            return;\n         default:\n            System.out.println(o);// 24\n            return;\n      }\n   }\n\n   public static void testObject3(Object o) {\n      label33:\n      while(true) {\n         if (o.hashCode() == 1) {// 30\n            switch (o) {// 31\n               case String var3 when var3.isEmpty() && var3.getBytes().length == 2:// 32\n                  System.out.println(\"empty s\");// 33\n                  break;// 34\n               case String var4:\n                  System.out.println(\"s\");// 36\n                  continue label33;// 37\n               case Integer var5:\n                  System.out.println(\"i\");// 39\n                  break;// 40\n               default:\n                  System.out.println(o);// 42\n                  break;// 43\n            }\n         }\n\n         System.out.println(\"1\");// 47\n         return;// 48\n      }\n   }\n}\n\nclass 'pkg/TestSwitchGuardedJavac' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n   }\n\n   method 'testObject (Ljava/lang/Object;)V' {\n      10      7\n      32      8\n      39      8\n      3c      8\n      3d      8\n      3e      8\n      46      8\n      49      8\n      4b      8\n      57      9\n      5a      9\n      5c      9\n      68      10\n      6b      10\n      6d      10\n      76      11\n      7a      11\n      80      14\n      83      14\n      85      14\n      88      15\n   }\n\n   method 'testObject2 (Ljava/lang/Object;)V' {\n      10      18\n      32      19\n      39      19\n      3c      19\n      3d      19\n      3e      19\n      46      20\n      49      20\n      4b      20\n      57      23\n      5a      23\n      5c      23\n      68      26\n      6b      26\n      6d      26\n      76      29\n      7a      29\n      80      21\n   }\n\n   method 'testObject3 (Ljava/lang/Object;)V' {\n      1      37\n      4      37\n      5      37\n      18      38\n      3a      39\n      41      39\n      44      39\n      45      39\n      46      39\n      4e      40\n      51      40\n      53      40\n      56      41\n      5f      43\n      62      43\n      64      43\n      67      44\n      70      46\n      73      46\n      75      46\n      78      47\n      7e      49\n      82      49\n      85      50\n      8b      54\n      8e      54\n      90      54\n      93      55\n   }\n}\n\nLines mapping:\n7 <-> 5\n10 <-> 8\n11 <-> 9\n12 <-> 10\n13 <-> 11\n14 <-> 12\n16 <-> 15\n17 <-> 16\n20 <-> 19\n21 <-> 21\n22 <-> 24\n23 <-> 27\n24 <-> 30\n26 <-> 22\n30 <-> 38\n31 <-> 39\n32 <-> 40\n33 <-> 41\n34 <-> 42\n36 <-> 44\n37 <-> 45\n39 <-> 47\n40 <-> 48\n42 <-> 50\n43 <-> 51\n47 <-> 55\n48 <-> 56\nNot mapped:\n35\n38\n41\n45\n"
  },
  {
    "path": "testData/results/TestSwitchNestedDeconstructionsJavac.dec",
    "content": "package pkg;\n\npublic class TestSwitchNestedDeconstructionsJavac {\n   public static void main(String[] args) {\n   }// 6\n\n   public static void testNestedSwitches(Object o) {\n      switch (o) {// 14\n         case R2(String s, String var5) when s.length() > 123456789:// 15\n            if (s.length() == 2) {// 16\n               System.out.println(o);// 17\n            }\n\n            return;// 42\n         case R2(String s, String var6) when s.length() > 2:// 20\n            if (s.length() == 2) {// 21\n               System.out.println(o);// 22\n            }\n\n            return;\n         case R2(String s, Object var24) when s.length() > 45:// 25\n            if (s.length() == 2) {// 26\n               System.out.println(o);// 27\n            }\n\n            return;\n         case R2(Object s, Object var23) when s.hashCode() > 7:// 30\n            if (s.hashCode() == 2) {// 31\n               System.out.println(o);// 32\n            }\n\n            return;\n         case R2(Object s, Object var25):\n            if (s.hashCode() == 2) {// 36\n               System.out.println(o);// 37\n            }\n\n            return;\n         default:\n            System.out.println(o);// 40\n            return;\n      }\n   }\n\n   public static void testStringString(Object o) {\n      switch (o) {// 45\n         case R2(String var4, String s2):\n            if (s2.isEmpty()) {// 47\n               System.out.println(\"3\");// 48\n            }\n            break;\n         default:\n            System.out.println(\"3\");// 51\n            break;\n      }\n\n      System.out.println(\"1\");// 53\n   }\n\n   public static void testNestedLevel2(Object o) {\n      switch (o) {// 57\n         case R2(String s, String var5) when var5.length() > 11:// 58\n            if (s.length() == 9) {// 59\n               System.out.println(o);// 60\n            }\n\n            return;// 70\n         case R2(Object s, R1(String var8)) when var8.length() > 7:// 63\n            if (s.hashCode() == 2) {// 64\n               System.out.println(o);// 65\n            }\n\n            return;\n         default:\n            System.out.println(o);// 68\n            return;\n      }\n   }\n\n   public static void testNumberString(Object o) {\n      switch (o) {// 73\n         case R2(String s, String var5) when var5.length() > 10:// 74\n            if (s.length() == 9) {// 75\n               System.out.println(o);// 76\n            }\n\n            return;// 91\n         case R2(Number s, String var7) when var7.length() > 9:// 79\n            if (s.hashCode() == 9) {// 80\n               System.out.println(o);// 81\n            }\n\n            return;\n         case R2(String s, Object var18) when s.length() > 7:// 84\n            if (s.length() == 2) {// 85\n               System.out.println(o);// 86\n            }\n\n            return;\n         default:\n            System.out.println(o);// 89\n            return;\n      }\n   }\n\n   public static void test2DeepDeconstruction(Object o) {\n      switch (o) {// 94\n         case R1(R1(String var5)) when var5.hashCode() == 5 -> System.out.println(\"123456789\");// 95 96\n         case R1(String var6) when var6.hashCode() == 3 -> System.out.println(\"3\");// 98 99\n         default -> System.out.println(\"3\");// 101\n      }\n\n      System.out.println(\"1\");// 103\n   }\n\n   public static void testDoubleLongCase(Object o) {\n      switch (o) {// 107\n         case R2(String s, String var5) when s.length() > 3:// 108\n            if (s.length() == 2) {// 109\n               System.out.println(o);// 110\n            }\n            break;\n         case R2(String s, String var6) when s.length() > 4:// 113\n            if (s.length() == 2) {// 114\n               System.out.println(o);// 115\n            }\n            break;\n         case R2(String s, Object var29) when s.length() > 3:// 118\n            if (s.length() == 2) {// 119\n               System.out.println(o);// 120\n            }\n            break;\n         case R2(Object s, Object var28) when s.hashCode() > 3:// 123\n            if (s.hashCode() == 2) {// 124\n               System.out.println(o);// 125\n            }\n            break;\n         case R2(Object s, Object var30):\n            if (s.hashCode() == 2) {// 129\n               System.out.println(o);// 130\n            }\n            break;\n         default:\n            System.out.println(o);\n            break;// 133\n      }\n\n      switch (o) {// 136\n         case R2(String s, String var5) when s.length() > 3:// 137\n            if (s.length() == 2) {// 138\n               System.out.println(o);// 139\n            }\n\n            return;// 164\n         case R2(String s, String var6) when s.length() > 4:// 142\n            if (s.length() == 2) {// 143\n               System.out.println(o);// 144\n            }\n\n            return;\n         case R2(String s, Object var33) when s.length() > 3:// 147\n            if (s.length() == 2) {// 148\n               System.out.println(o);// 149\n            }\n\n            return;\n         case R2(Object s, Object var32) when s.hashCode() > 3:// 152\n            if (s.hashCode() == 2) {// 153\n               System.out.println(o);// 154\n            }\n\n            return;\n         case R2(Object s, Object var34):\n            if (s.hashCode() == 2) {// 158\n               System.out.println(o);// 159\n            }\n\n            return;\n         default:\n            System.out.println(o);// 162\n            return;\n      }\n   }\n\n   public static void testTryWithDoubleDo(Object o, Object o2) {\n      try {\n         System.out.println(\"1\");// 168\n      } catch (Exception var16) {// 169\n         switch (o2) {// 170\n            case R2(Number var6, String var7):\n               System.out.println(\"10\");// 171 172\n               return;// 180\n            case R2(String var8, Number var9):\n               System.out.println(o);// 174 175\n               return;\n            default:\n               System.out.println(o);// 177\n               return;\n         }\n      }\n\n   }\n\n   static record R2(Object o, Object o2) {\n      R2(Object o, Object o2) {\n         this.o = o;\n         this.o2 = o2;\n      }\n\n      public Object o() {\n         return this.o;\n      }\n\n      public Object o2() {\n         return this.o2;// 183\n      }\n   }\n\n   static record R1(Object o) {\n      R1(Object o) {\n         this.o = o;\n      }\n\n      public Object o() {\n         return this.o;// 8\n      }\n   }\n}\n\nclass 'pkg/TestSwitchNestedDeconstructionsJavac' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n   }\n\n   method 'testNestedSwitches (Ljava/lang/Object;)V' {\n      10      7\n      9d      8\n      a0      8\n      a2      8\n      ad      9\n      b0      9\n      b1      9\n      b4      10\n      b8      10\n      c7      14\n      ca      14\n      cb      14\n      d6      15\n      d9      15\n      da      15\n      dd      16\n      e1      16\n      ed      20\n      f0      20\n      f2      20\n      fd      21\n      100      21\n      101      21\n      104      22\n      108      22\n      124      26\n      127      26\n      129      26\n      134      27\n      137      27\n      138      27\n      13b      28\n      13f      28\n      168      33\n      16b      33\n      16c      33\n      16f      34\n      173      34\n      17c      39\n      180      39\n      197      13\n   }\n\n   method 'testStringString (Ljava/lang/Object;)V' {\n      10      45\n      5b      47\n      5d      47\n      63      48\n      66      48\n      68      48\n      6e      52\n      71      52\n      73      52\n      76      53\n      79      56\n      7c      56\n      7e      56\n      81      57\n   }\n\n   method 'testNestedLevel2 (Ljava/lang/Object;)V' {\n      10      60\n      72      61\n      75      61\n      77      61\n      82      62\n      85      62\n      87      62\n      8a      63\n      8e      63\n      c5      67\n      c8      67\n      ca      67\n      d5      68\n      d8      68\n      d9      68\n      dc      69\n      e0      69\n      ee      74\n      f2      74\n      109      66\n   }\n\n   method 'testNumberString (Ljava/lang/Object;)V' {\n      10      80\n      76      81\n      79      81\n      7b      81\n      86      82\n      89      82\n      8b      82\n      8e      83\n      92      83\n      b6      87\n      b9      87\n      bb      87\n      c6      88\n      c9      88\n      cb      88\n      ce      89\n      d2      89\n      eb      93\n      ee      93\n      f0      93\n      fb      94\n      fe      94\n      ff      94\n      102      95\n      106      95\n      114      100\n      118      100\n      12f      86\n   }\n\n   method 'test2DeepDeconstruction (Ljava/lang/Object;)V' {\n      10      106\n      73      107\n      76      107\n      77      107\n      80      107\n      83      107\n      85      107\n      94      108\n      97      108\n      98      108\n      a1      108\n      a4      108\n      a6      108\n      b1      109\n      b4      109\n      b6      109\n      bc      112\n      bf      112\n      c1      112\n      c4      113\n   }\n\n   method 'testDoubleLongCase (Ljava/lang/Object;)V' {\n      10      116\n      9d      117\n      a0      117\n      a1      117\n      ac      118\n      af      118\n      b0      118\n      b3      119\n      b7      119\n      c6      122\n      c9      122\n      ca      122\n      d5      123\n      d8      123\n      d9      123\n      dc      124\n      e0      124\n      ec      127\n      ef      127\n      f0      127\n      fb      128\n      fe      128\n      ff      128\n      102      129\n      106      129\n      122      132\n      125      132\n      126      132\n      131      133\n      134      133\n      135      133\n      138      134\n      13c      134\n      165      138\n      168      138\n      169      138\n      16c      139\n      170      139\n      179      143\n      17d      143\n      180      144\n      193      147\n      21d      148\n      220      148\n      221      148\n      22c      149\n      22f      149\n      230      149\n      233      150\n      237      150\n      246      154\n      249      154\n      24a      154\n      255      155\n      258      155\n      259      155\n      25c      156\n      260      156\n      26c      160\n      26f      160\n      270      160\n      27b      161\n      27e      161\n      27f      161\n      282      162\n      286      162\n      2a2      166\n      2a5      166\n      2a6      166\n      2b1      167\n      2b4      167\n      2b5      167\n      2b8      168\n      2bc      168\n      2e5      173\n      2e8      173\n      2e9      173\n      2ec      174\n      2f0      174\n      2f9      179\n      2fd      179\n      314      153\n   }\n\n   method 'testTryWithDoubleDo (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      0      186\n      3      186\n      5      186\n      b      187\n      1e      188\n      81      190\n      8a      190\n      8d      190\n      b2      193\n      be      193\n      ce      196\n      d2      196\n      e9      191\n   }\n}\n\nclass 'pkg/TestSwitchNestedDeconstructionsJavac$R2' {\n   method '<init> (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      6      205\n      b      206\n      e      207\n   }\n\n   method 'o ()Ljava/lang/Object;' {\n      1      210\n      4      210\n   }\n\n   method 'o2 ()Ljava/lang/Object;' {\n      1      214\n      4      214\n   }\n}\n\nclass 'pkg/TestSwitchNestedDeconstructionsJavac$R1' {\n   method '<init> (Ljava/lang/Object;)V' {\n      6      220\n      9      221\n   }\n\n   method 'o ()Ljava/lang/Object;' {\n      1      224\n      4      224\n   }\n}\n\nLines mapping:\n6 <-> 5\n8 <-> 225\n14 <-> 8\n15 <-> 9\n16 <-> 10\n17 <-> 11\n20 <-> 15\n21 <-> 16\n22 <-> 17\n25 <-> 21\n26 <-> 22\n27 <-> 23\n30 <-> 27\n31 <-> 28\n32 <-> 29\n36 <-> 34\n37 <-> 35\n40 <-> 40\n42 <-> 14\n45 <-> 46\n47 <-> 48\n48 <-> 49\n51 <-> 53\n53 <-> 57\n57 <-> 61\n58 <-> 62\n59 <-> 63\n60 <-> 64\n63 <-> 68\n64 <-> 69\n65 <-> 70\n68 <-> 75\n70 <-> 67\n73 <-> 81\n74 <-> 82\n75 <-> 83\n76 <-> 84\n79 <-> 88\n80 <-> 89\n81 <-> 90\n84 <-> 94\n85 <-> 95\n86 <-> 96\n89 <-> 101\n91 <-> 87\n94 <-> 107\n95 <-> 108\n96 <-> 108\n98 <-> 109\n99 <-> 109\n101 <-> 110\n103 <-> 113\n107 <-> 117\n108 <-> 118\n109 <-> 119\n110 <-> 120\n113 <-> 123\n114 <-> 124\n115 <-> 125\n118 <-> 128\n119 <-> 129\n120 <-> 130\n123 <-> 133\n124 <-> 134\n125 <-> 135\n129 <-> 139\n130 <-> 140\n133 <-> 145\n136 <-> 148\n137 <-> 149\n138 <-> 150\n139 <-> 151\n142 <-> 155\n143 <-> 156\n144 <-> 157\n147 <-> 161\n148 <-> 162\n149 <-> 163\n152 <-> 167\n153 <-> 168\n154 <-> 169\n158 <-> 174\n159 <-> 175\n162 <-> 180\n164 <-> 154\n168 <-> 187\n169 <-> 188\n170 <-> 189\n171 <-> 191\n172 <-> 191\n174 <-> 194\n175 <-> 194\n177 <-> 197\n180 <-> 192\n183 <-> 215\nNot mapped:\n35\n46\n54\n97\n100\n104\n128\n157\n173\n176\n179\n"
  },
  {
    "path": "testData/results/TestSwitchOnEnum.dec",
    "content": "package pkg;\n\nimport java.util.concurrent.TimeUnit;\n\npublic class TestSwitchOnEnum {\n   int myInt;\n\n   public int testSOE(TimeUnit t) {\n      switch (t) {// 14\n         case MICROSECONDS:\n            return 2;// 16\n         case SECONDS:\n            return 1;// 18\n         default:\n            return 0;// 20\n      }\n   }\n\n   static class Example {\n      void test(A a, B b) {\n         switch (a) {// 30\n            case A1:\n               System.out.println(\"A1\");// 32\n               break;// 33\n            case A2:\n               System.out.println(\"A2\");// 35\n         }\n\n         switch (b) {// 38\n            case B1:\n               System.out.println(\"B1\");// 40\n               break;// 41\n            case B2:\n               System.out.println(\"B2\");// 43\n         }\n\n      }// 46\n\n      static enum B {\n         B1,\n         B2;\n      }\n\n      static enum A {\n         A1,\n         A2;\n      }\n   }\n}\n\nclass 'pkg/TestSwitchOnEnum' {\n   method 'testSOE (Ljava/util/concurrent/TimeUnit;)I' {\n      8      8\n      24      10\n      25      10\n      26      12\n      27      12\n      28      14\n      29      14\n   }\n}\n\nclass 'pkg/TestSwitchOnEnum$Example' {\n   method 'test (Lpkg/TestSwitchOnEnum$Example$A;Lpkg/TestSwitchOnEnum$Example$B;)V' {\n      8      20\n      24      22\n      27      22\n      29      22\n      2c      23\n      2f      25\n      32      25\n      34      25\n      3f      28\n      58      30\n      5b      30\n      5d      30\n      60      31\n      63      33\n      66      33\n      68      33\n      6b      36\n   }\n}\n\nLines mapping:\n14 <-> 9\n16 <-> 11\n18 <-> 13\n20 <-> 15\n30 <-> 21\n32 <-> 23\n33 <-> 24\n35 <-> 26\n38 <-> 29\n40 <-> 31\n41 <-> 32\n43 <-> 34\n46 <-> 37\n"
  },
  {
    "path": "testData/results/TestSwitchOnEnumEclipse.dec",
    "content": "package pkg;\n\nimport java.util.concurrent.TimeUnit;\n\npublic class TestSwitchOnEnumEclipse {\n   int myInt;\n\n   public int testSOE(TimeUnit t) {\n      switch (t) {// 11\n         case MICROSECONDS:\n            return 2;// 13\n         case MILLISECONDS:\n         default:\n            return 0;// 17\n         case SECONDS:\n            return 1;// 15\n      }\n   }\n\n   static class Example {\n      void test(A a, B b) {\n         switch (a) {// 27\n            case A1:\n               System.out.println(\"A1\");// 29\n               break;// 30\n            case A2:\n               System.out.println(\"A2\");// 32\n         }\n\n         switch (b) {// 35\n            case B1:\n               System.out.println(\"B1\");// 37\n               break;// 38\n            case B2:\n               System.out.println(\"B2\");// 40\n         }\n\n      }// 43\n\n      static enum A {\n         A1,\n         A2;\n      }\n\n      static enum B {\n         B1,\n         B2;\n      }\n   }\n}\n\nclass 'pkg/TestSwitchOnEnumEclipse' {\n   method 'testSOE (Ljava/util/concurrent/TimeUnit;)I' {\n      8      8\n      24      10\n      25      10\n      26      15\n      27      15\n      28      13\n      29      13\n   }\n}\n\nclass 'pkg/TestSwitchOnEnumEclipse$Example' {\n   method 'test (Lpkg/TestSwitchOnEnumEclipse$Example$A;Lpkg/TestSwitchOnEnumEclipse$Example$B;)V' {\n      8      21\n      20      23\n      23      23\n      25      23\n      28      24\n      2b      26\n      2e      26\n      30      26\n      3b      29\n      50      31\n      53      31\n      55      31\n      58      32\n      5b      34\n      5e      34\n      60      34\n      63      37\n   }\n}\n\nLines mapping:\n11 <-> 9\n13 <-> 11\n15 <-> 16\n17 <-> 14\n27 <-> 22\n29 <-> 24\n30 <-> 25\n32 <-> 27\n35 <-> 30\n37 <-> 32\n38 <-> 33\n40 <-> 35\n43 <-> 38\nNot mapped:\n34\n"
  },
  {
    "path": "testData/results/TestSwitchOnStringsEcj.dec",
    "content": "package pkg;\n\npublic class TestSwitchOnStringsEcj {\n   String s;\n   static final String S = \"\";\n\n   void noCase() {\n      this.getStr().hashCode();// 8\n   }// 10\n\n   void oneCase(String s) {\n      System.out.println(1);// 13\n      switch (s) {// 14\n         case \"xxx\":\n            System.out.println(2);// 16\n         default:\n            System.out.println(3);// 19\n      }\n   }// 20\n\n   void oneCaseWithDefault() {\n      System.out.println(1);// 23\n      switch (this.s) {// 24\n         case \"xxx\":\n            System.out.println(2);// 26\n            break;// 27\n         default:\n            System.out.println(3);// 29\n      }\n\n      System.out.println(4);// 32\n   }// 33\n\n   void multipleCases1() {\n      System.out.println(1);// 36\n      switch (\"\") {// 37\n         case \"xxx\":\n            System.out.println(2);// 39\n            break;\n         case \"yyy\":\n            System.out.println(3);// 42\n      }\n\n      System.out.println(4);// 45\n   }// 46\n\n   void multipleCasesWithDefault1() {\n      label19: {\n         System.out.println(1);// 49\n         switch (this.getStr()) {// 50\n            case \"xxx\":\n               System.out.println(2);// 52\n               break label19;// 53\n            case \"yyy\":\n               System.out.println(3);// 55\n               break label19;// 56\n         }\n\n         System.out.println(4);// 58\n      }\n\n      System.out.println(5);// 61\n   }// 62\n\n   void multipleCases2() {\n      System.out.println(1);// 65\n      switch (\"\") {// 66\n         case \"xxx\":\n            System.out.println(2);// 68\n            break;\n         case \"yyy\":\n            System.out.println(3);// 71\n            break;\n         case \"zzz\":\n            System.out.println(4);// 74\n      }\n\n      System.out.println(5);// 77\n   }// 78\n\n   void multipleCasesWithDefault2() {\n      label23: {\n         System.out.println(1);// 81\n         switch (this.getStr()) {// 82\n            case \"xxx\":\n               System.out.println(2);// 84\n               break label23;// 85\n            case \"yyy\":\n               System.out.println(3);// 87\n               break label23;// 88\n            case \"zzz\":\n               System.out.println(4);// 90\n               break label23;// 91\n         }\n\n         System.out.println(5);// 93\n      }\n\n      System.out.println(6);// 96\n   }// 97\n\n   String getStr() {\n      return \"\";// 149\n   }\n}\n\nclass 'pkg/TestSwitchOnStringsEcj' {\n   method 'noCase ()V' {\n      1      7\n      6      7\n      a      8\n   }\n\n   method 'oneCase (Ljava/lang/String;)V' {\n      0      11\n      3      11\n      4      11\n      d      12\n      2c      14\n      2f      14\n      30      14\n      33      16\n      36      16\n      37      16\n      3a      18\n   }\n\n   method 'oneCaseWithDefault ()V' {\n      0      21\n      3      21\n      4      21\n      8      22\n      10      22\n      30      24\n      33      24\n      34      24\n      37      25\n      3a      27\n      3d      27\n      3e      27\n      41      30\n      44      30\n      45      30\n      48      31\n   }\n\n   method 'multipleCases1 ()V' {\n      0      34\n      3      34\n      4      34\n      7      35\n      e      35\n      40      37\n      43      37\n      44      37\n      4a      40\n      4d      40\n      4e      40\n      51      43\n      54      43\n      55      43\n      58      44\n   }\n\n   method 'multipleCasesWithDefault1 ()V' {\n      0      48\n      3      48\n      4      48\n      8      49\n      10      49\n      44      51\n      47      51\n      48      51\n      4b      52\n      4e      54\n      51      54\n      52      54\n      55      55\n      58      58\n      5b      58\n      5c      58\n      5f      61\n      62      61\n      63      61\n      66      62\n   }\n\n   method 'multipleCases2 ()V' {\n      0      65\n      3      65\n      4      65\n      7      66\n      e      66\n      54      68\n      57      68\n      58      68\n      5e      71\n      61      71\n      62      71\n      68      74\n      6b      74\n      6c      74\n      6f      77\n      72      77\n      73      77\n      76      78\n   }\n\n   method 'multipleCasesWithDefault2 ()V' {\n      0      82\n      3      82\n      4      82\n      8      83\n      10      83\n      58      85\n      5b      85\n      5c      85\n      5f      86\n      62      88\n      65      88\n      66      88\n      69      89\n      6c      91\n      6f      91\n      70      91\n      73      92\n      76      95\n      79      95\n      7a      95\n      7d      98\n      80      98\n      82      98\n      85      99\n   }\n\n   method 'getStr ()Ljava/lang/String;' {\n      0      102\n      2      102\n   }\n}\n\nLines mapping:\n8 <-> 8\n10 <-> 9\n13 <-> 12\n14 <-> 13\n16 <-> 15\n19 <-> 17\n20 <-> 19\n23 <-> 22\n24 <-> 23\n26 <-> 25\n27 <-> 26\n29 <-> 28\n32 <-> 31\n33 <-> 32\n36 <-> 35\n37 <-> 36\n39 <-> 38\n42 <-> 41\n45 <-> 44\n46 <-> 45\n49 <-> 49\n50 <-> 50\n52 <-> 52\n53 <-> 53\n55 <-> 55\n56 <-> 56\n58 <-> 59\n61 <-> 62\n62 <-> 63\n65 <-> 66\n66 <-> 67\n68 <-> 69\n71 <-> 72\n74 <-> 75\n77 <-> 78\n78 <-> 79\n81 <-> 83\n82 <-> 84\n84 <-> 86\n85 <-> 87\n87 <-> 89\n88 <-> 90\n90 <-> 92\n91 <-> 93\n93 <-> 96\n96 <-> 99\n97 <-> 100\n149 <-> 103\nNot mapped:\n40\n69\n72\n"
  },
  {
    "path": "testData/results/TestSwitchOnStringsJavac.dec",
    "content": "package pkg;\n\nimport java.util.Objects;\n\npublic class TestSwitchOnStringsJavac {\n   String s;\n   static final String S = \"\";\n\n   void noCase() {\n      Objects.requireNonNull(this.getStr());// 8\n   }// 10\n\n   void oneCase(String s) {\n      System.out.println(1);// 13\n      switch (s) {// 14\n         case \"xxx\":\n            System.out.println(2);// 16\n         default:\n            System.out.println(3);// 19\n      }\n   }// 20\n\n   void oneCaseWithDefault() {\n      System.out.println(1);// 23\n      switch (this.s) {// 24\n         case \"xxx\" -> System.out.println(2);// 26\n         default -> System.out.println(3);// 29\n      }\n\n      System.out.println(4);// 32\n   }// 33\n\n   void multipleCases1() {\n      System.out.println(1);// 36\n      switch (\"\") {// 37\n         case \"xxx\" -> System.out.println(2);// 39\n         case \"yyy\" -> System.out.println(3);// 42\n      }\n\n      System.out.println(4);// 45\n   }// 46\n\n   void multipleCasesWithDefault1() {\n      System.out.println(1);// 49\n      switch (this.getStr()) {// 50\n         case \"xxx\" -> System.out.println(2);// 52\n         case \"yyy\" -> System.out.println(3);// 55\n         default -> System.out.println(4);// 58\n      }\n\n      System.out.println(5);// 61\n   }// 62\n\n   void multipleCases2() {\n      System.out.println(1);// 65\n      switch (\"\") {// 66\n         case \"xxx\" -> System.out.println(2);// 68\n         case \"yyy\" -> System.out.println(3);// 71\n         case \"zzz\" -> System.out.println(4);// 74\n      }\n\n      System.out.println(5);// 77\n   }// 78\n\n   void multipleCasesWithDefault2() {\n      System.out.println(1);// 81\n      switch (this.getStr()) {// 82\n         case \"xxx\" -> System.out.println(2);// 84\n         case \"yyy\" -> System.out.println(3);// 87\n         case \"zzz\" -> System.out.println(4);// 90\n         default -> System.out.println(5);// 93\n      }\n\n      System.out.println(6);// 96\n   }// 97\n\n   void combined() {\n      System.out.println(\"started\");// 101\n      if (this.s.length() > 0) {// 102\n         System.out.println();// 103\n         switch (this.s) {// 104\n            case \"b\" -> System.out.println(1);// 105\n            case \"d\" -> System.out.println(2);// 106\n            case \"a\" -> System.out.println(3);// 107\n            case \"f\" -> System.out.println(4);// 108\n            default -> System.out.println(Math.random());// 109\n         }\n\n         System.out.println(this.s);// 111\n         this.combined();// 112\n      } else {\n         try {\n            label109: {\n               switch (this.getStr()) {// 115\n                  case \"h\":\n                  case \"i\":\n                     while(true) {\n                        if (this.s == null) {// 118\n                           System.out.println(5);// 127\n                           break;\n                        }\n\n                        try {\n                           if (this.s.length() == 1) {// 120\n                              System.out.println(this.s);// 121\n                           }\n                        } catch (NullPointerException var5) {// 123\n                           System.out.println(var5.getMessage());// 124\n                        }\n                     }\n                  case \"j\":\n                  case \"f\":\n                     break;\n                  default:\n                     System.out.println(7);// 133\n                     break label109;\n               }\n\n               System.out.println(6);// 130\n               return;// 131\n            }\n         } catch (NullPointerException var6) {// 135\n            NullPointerException e = var6;\n            int i = 0;\n\n            while(i < 10) {\n               switch (this.getStr()) {// 137\n                  case \"\" -> System.out.println(8);// 138\n                  default -> System.out.println(e.getMessage());// 139\n               }\n\n               ++i;// 136\n            }\n\n            System.out.println(9);// 142\n         }\n      }\n\n      System.out.println(\"finished\");// 145\n   }// 146\n\n   String getStr() {\n      return \"\";// 150\n   }\n}\n\nclass 'pkg/TestSwitchOnStringsJavac' {\n   method 'noCase ()V' {\n      1      9\n      5      9\n      a      10\n   }\n\n   method 'oneCase (Ljava/lang/String;)V' {\n      0      13\n      3      13\n      4      13\n      2c      14\n      40      16\n      43      16\n      44      16\n      47      18\n      4a      18\n      4b      18\n      4e      20\n   }\n\n   method 'oneCaseWithDefault ()V' {\n      0      23\n      3      23\n      4      23\n      8      24\n      30      24\n      44      25\n      47      25\n      48      25\n      4e      26\n      51      26\n      52      26\n      55      29\n      58      29\n      59      29\n      5c      30\n   }\n\n   method 'multipleCases1 ()V' {\n      0      33\n      3      33\n      4      33\n      7      34\n      46      34\n      60      35\n      63      35\n      64      35\n      6a      36\n      6d      36\n      6e      36\n      71      39\n      74      39\n      75      39\n      78      40\n   }\n\n   method 'multipleCasesWithDefault1 ()V' {\n      0      43\n      3      43\n      4      43\n      8      44\n      46      44\n      60      45\n      63      45\n      64      45\n      6a      46\n      6d      46\n      6e      46\n      74      47\n      77      47\n      78      47\n      7b      50\n      7e      50\n      7f      50\n      82      51\n   }\n\n   method 'multipleCases2 ()V' {\n      0      54\n      3      54\n      4      54\n      7      55\n      5c      55\n      78      56\n      7b      56\n      7c      56\n      82      57\n      85      57\n      86      57\n      8c      58\n      8f      58\n      90      58\n      93      61\n      96      61\n      97      61\n      9a      62\n   }\n\n   method 'multipleCasesWithDefault2 ()V' {\n      0      65\n      3      65\n      4      65\n      8      66\n      5c      66\n      78      67\n      7b      67\n      7c      67\n      82      68\n      85      68\n      86      68\n      8c      69\n      8f      69\n      90      69\n      96      70\n      99      70\n      9a      70\n      9d      73\n      a0      73\n      a2      73\n      a5      74\n   }\n\n   method 'combined ()V' {\n      0      77\n      3      77\n      5      77\n      9      78\n      c      78\n      f      78\n      12      79\n      15      79\n      19      80\n      7e      80\n      9c      81\n      9f      81\n      a0      81\n      a6      82\n      a9      82\n      aa      82\n      b0      83\n      b3      83\n      b4      83\n      ba      84\n      bd      84\n      be      84\n      c4      85\n      c7      85\n      ca      85\n      cd      88\n      d1      88\n      d4      88\n      d8      89\n      df      93\n      142      93\n      161      97\n      164      97\n      168      103\n      16b      103\n      16e      103\n      16f      103\n      172      104\n      176      104\n      179      104\n      17f      106\n      180      107\n      184      107\n      187      107\n      18d      98\n      190      98\n      191      98\n      194      118\n      197      118\n      199      118\n      19c      119\n      19d      114\n      1a0      114\n      1a2      114\n      1a8      121\n      1a9      123\n      1aa      123\n      1ac      125\n      1ae      125\n      1b2      126\n      1de      126\n      1f0      127\n      1f3      127\n      1f5      127\n      1fb      128\n      1ff      128\n      202      128\n      205      131\n      20b      134\n      20e      134\n      210      134\n      213      138\n      216      138\n      218      138\n      21b      139\n   }\n\n   method 'getStr ()Ljava/lang/String;' {\n      0      142\n      2      142\n   }\n}\n\nLines mapping:\n8 <-> 10\n10 <-> 11\n13 <-> 14\n14 <-> 15\n16 <-> 17\n19 <-> 19\n20 <-> 21\n23 <-> 24\n24 <-> 25\n26 <-> 26\n29 <-> 27\n32 <-> 30\n33 <-> 31\n36 <-> 34\n37 <-> 35\n39 <-> 36\n42 <-> 37\n45 <-> 40\n46 <-> 41\n49 <-> 44\n50 <-> 45\n52 <-> 46\n55 <-> 47\n58 <-> 48\n61 <-> 51\n62 <-> 52\n65 <-> 55\n66 <-> 56\n68 <-> 57\n71 <-> 58\n74 <-> 59\n77 <-> 62\n78 <-> 63\n81 <-> 66\n82 <-> 67\n84 <-> 68\n87 <-> 69\n90 <-> 70\n93 <-> 71\n96 <-> 74\n97 <-> 75\n101 <-> 78\n102 <-> 79\n103 <-> 80\n104 <-> 81\n105 <-> 82\n106 <-> 83\n107 <-> 84\n108 <-> 85\n109 <-> 86\n111 <-> 89\n112 <-> 90\n115 <-> 94\n118 <-> 98\n120 <-> 104\n121 <-> 105\n123 <-> 107\n124 <-> 108\n127 <-> 99\n130 <-> 119\n131 <-> 120\n133 <-> 115\n135 <-> 122\n136 <-> 132\n137 <-> 127\n138 <-> 128\n139 <-> 129\n142 <-> 135\n145 <-> 139\n146 <-> 140\n150 <-> 143\nNot mapped:\n27\n40\n53\n56\n69\n72\n85\n88\n91\n125\n143\n"
  },
  {
    "path": "testData/results/TestSwitchRules.dec",
    "content": "package pkg;\n\npublic class TestSwitchRules {\n   public static void main(String[] args) {\n      test1(\"1\");// 5\n      test2(\"1\");// 6\n      test3(\"1\");// 7\n   }// 8\n\n   private static void test1(String r2) {\n      switch (r2) {// 12\n         case \"2\" -> System.out.println(\"4\");// 13\n         case \"3\" -> System.out.println(\"2\");// 14\n         default -> System.out.println(\"31\");// 15\n      }\n\n   }// 17\n\n   private static void test2(String r2) {\n      switch (r2) {// 20\n         case \"2\" -> { }\n         case \"3\" -> System.out.println(\"2\");// 27\n         default -> System.out.println(\"31\");// 24\n      }\n\n   }// 30\n\n   private static void test3(String r2) {\n      switch (r2) {// 33\n         case \"2\":\n         default:\n            System.out.println(\"31\");// 36\n            break;// 37\n         case \"3\":\n            System.out.println(\"2\");// 39\n      }\n\n   }// 42\n}\n\nclass 'pkg/TestSwitchRules' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n      2      4\n      5      5\n      7      5\n      a      6\n      c      6\n      f      7\n   }\n\n   method 'test1 (Ljava/lang/String;)V' {\n      3e      10\n      58      11\n      5b      11\n      5d      11\n      63      12\n      66      12\n      68      12\n      6e      13\n      71      13\n      73      13\n      76      16\n   }\n\n   method 'test2 (Ljava/lang/String;)V' {\n      3e      19\n      5b      22\n      5e      22\n      60      22\n      66      21\n      69      21\n      6b      21\n      6e      25\n   }\n\n   method 'test3 (Ljava/lang/String;)V' {\n      3e      28\n      58      31\n      5b      31\n      5d      31\n      60      32\n      63      34\n      66      34\n      68      34\n      6b      37\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 6\n7 <-> 7\n8 <-> 8\n12 <-> 11\n13 <-> 12\n14 <-> 13\n15 <-> 14\n17 <-> 17\n20 <-> 20\n24 <-> 23\n27 <-> 22\n30 <-> 26\n33 <-> 29\n36 <-> 32\n37 <-> 33\n39 <-> 35\n42 <-> 38\nNot mapped:\n22\n25\n"
  },
  {
    "path": "testData/results/TestSwitchSimpleReferencesJavac.dec",
    "content": "package pkg;\n\npublic class TestSwitchSimpleReferencesJavac {\n   private static void testString(String r2) {\n      switch (r2) {// 6\n         case \"first\":\n            System.out.println(\"1\");// 8\n            break;// 9\n         case \"second\":\n            System.out.println(\"2\");// 11\n         case \"third\":\n            System.out.println(\"3\");// 13\n            break;// 14\n         case null:\n         default:\n            System.out.println(\"default null\");// 16\n      }\n\n   }// 18\n\n   private static void testString2(String r2) {\n      switch (r2) {// 21\n         case \"first\" -> System.out.println(\"1\");// 23\n         case \"second\" -> System.out.println(\"2\");// 26\n         case String s -> System.out.println(s);// 29\n      }\n\n   }// 31\n\n   private static void testString3(String r2) {\n      switch (r2) {// 34\n         case null -> System.out.println(\"null\");// 42\n         case \"first\" -> System.out.println(\"1\");// 36\n         case \"second\" -> System.out.println(\"2\");// 39\n         case String s -> System.out.println(s);// 45\n      }\n\n   }// 47\n\n   private static void testByte(Byte r2) {\n      switch (r2) {// 51\n         case 1:\n            break;\n         case 2:\n            System.out.println(\"2\");// 55\n            break;// 56\n         case null:\n         default:\n            System.out.println(\"default, null\");// 59\n      }\n\n   }// 61\n\n   private static void testChar(Character r2) {\n      switch (r2) {// 64\n         case '1':\n         case '2':\n            System.out.println(\"2\");// 67\n            break;// 68\n         case null:\n         default:\n            System.out.println(\"default, null\");// 71\n      }\n\n   }// 73\n\n   private static void testInt(Integer r2) {\n      switch (r2) {// 77\n         case 1:\n            break;\n         case 3:\n            System.out.println(\"2\");// 81\n            break;// 82\n         case null:\n         default:\n            System.out.println(\"default, null\");// 85\n      }\n\n   }// 87\n\n   private static void testInt2(Integer r2) {\n      switch (r2) {// 90\n         case 1 -> { }\n         case 3 -> System.out.println(\"2\");// 94\n         default -> System.out.println(\"default\");// 97\n      }\n\n   }// 99\n\n   private static void testEnum(Numbers r2) {\n      switch (r2) {\n         case null -> System.out.println(\"null\");// 113\n         case FIRST -> { }\n         case SECOND -> System.out.println(\"2\");// 110\n         default -> throw new MatchException((String)null, (Throwable)null);// 106\n      }\n\n   }// 115\n\n   private static void testEnum2(Numbers r2) {\n      switch (r2) {\n         case null -> System.out.println(\"null\");// 125\n         case FIRST -> { }\n         case SECOND -> System.out.println(\"2\");// 122\n         default -> throw new MatchException((String)null, (Throwable)null);// 118\n      }\n\n   }// 127\n\n   private static void testEnum3(Numbers r2) {\n      switch (r2) {// 130\n         case null -> System.out.println(\"null\");// 137\n         case FIRST -> { }\n         case SECOND -> System.out.println(\"2\");// 134\n         case Numbers n -> System.out.println(n);// 140\n      }\n\n   }// 142\n\n   private static void testEnum4(Numbers r2) {\n      switch (r2.ordinal()) {// 145\n         case 0 -> { }\n         case 1 -> System.out.println(\"2\");// 149\n         default -> System.out.println(\"default\");// 152\n      }\n\n   }// 154\n\n   private static void testEnum5(Numbers r2) {\n      switch (r2) {// 157\n         case SECOND:\n            System.out.println(\"2\");// 159\n            break;// 160\n         case null:\n         default:\n            System.out.println(\"default\");// 162\n      }\n\n   }// 164\n\n   static enum Numbers {\n      FIRST,\n      SECOND;\n   }\n}\n\nclass 'pkg/TestSwitchSimpleReferencesJavac' {\n   method 'testString (Ljava/lang/String;)V' {\n      b      4\n      28      6\n      2b      6\n      2d      6\n      30      7\n      33      9\n      36      9\n      38      9\n      3b      11\n      3e      11\n      40      11\n      43      12\n      46      15\n      49      15\n      4b      15\n      4e      18\n   }\n\n   method 'testString2 (Ljava/lang/String;)V' {\n      10      21\n      2c      22\n      2f      22\n      31      22\n      37      23\n      3a      23\n      3c      23\n      44      24\n      48      24\n      4b      27\n   }\n\n   method 'testString3 (Ljava/lang/String;)V' {\n      b      30\n      24      32\n      27      32\n      29      32\n      2f      33\n      32      33\n      34      33\n      3a      31\n      3d      31\n      3f      31\n      47      34\n      4b      34\n      4e      37\n   }\n\n   method 'testByte (Ljava/lang/Byte;)V' {\n      b      40\n      27      44\n      2a      44\n      2c      44\n      2f      45\n      32      48\n      35      48\n      37      48\n      3a      51\n   }\n\n   method 'testChar (Ljava/lang/Character;)V' {\n      b      54\n      24      57\n      27      57\n      29      57\n      2c      58\n      2f      61\n      32      61\n      34      61\n      37      64\n   }\n\n   method 'testInt (Ljava/lang/Integer;)V' {\n      b      67\n      27      71\n      2a      71\n      2c      71\n      2f      72\n      32      75\n      35      75\n      37      75\n      3a      78\n   }\n\n   method 'testInt2 (Ljava/lang/Integer;)V' {\n      1      81\n      4      81\n      23      83\n      26      83\n      28      83\n      2e      84\n      31      84\n      33      84\n      36      87\n   }\n\n   method 'testEnum (Lpkg/TestSwitchSimpleReferencesJavac$Numbers;)V' {\n      b      90\n      28      94\n      29      94\n      2d      94\n      31      93\n      34      93\n      36      93\n      3c      91\n      3f      91\n      41      91\n      44      97\n   }\n\n   method 'testEnum2 (Lpkg/TestSwitchSimpleReferencesJavac$Numbers;)V' {\n      b      100\n      28      104\n      29      104\n      2d      104\n      31      103\n      34      103\n      36      103\n      3c      101\n      3f      101\n      41      101\n      44      107\n   }\n\n   method 'testEnum3 (Lpkg/TestSwitchSimpleReferencesJavac$Numbers;)V' {\n      b      110\n      27      113\n      2a      113\n      2c      113\n      32      111\n      35      111\n      37      111\n      3f      114\n      43      114\n      46      117\n   }\n\n   method 'testEnum4 (Lpkg/TestSwitchSimpleReferencesJavac$Numbers;)V' {\n      1      120\n      4      120\n      23      122\n      26      122\n      28      122\n      2e      123\n      31      123\n      33      123\n      36      126\n   }\n\n   method 'testEnum5 (Lpkg/TestSwitchSimpleReferencesJavac$Numbers;)V' {\n      b      129\n      24      131\n      27      131\n      29      131\n      2c      132\n      2f      135\n      32      135\n      34      135\n      37      138\n   }\n}\n\nLines mapping:\n6 <-> 5\n8 <-> 7\n9 <-> 8\n11 <-> 10\n13 <-> 12\n14 <-> 13\n16 <-> 16\n18 <-> 19\n21 <-> 22\n23 <-> 23\n26 <-> 24\n29 <-> 25\n31 <-> 28\n34 <-> 31\n36 <-> 33\n39 <-> 34\n42 <-> 32\n45 <-> 35\n47 <-> 38\n51 <-> 41\n55 <-> 45\n56 <-> 46\n59 <-> 49\n61 <-> 52\n64 <-> 55\n67 <-> 58\n68 <-> 59\n71 <-> 62\n73 <-> 65\n77 <-> 68\n81 <-> 72\n82 <-> 73\n85 <-> 76\n87 <-> 79\n90 <-> 82\n94 <-> 84\n97 <-> 85\n99 <-> 88\n106 <-> 95\n110 <-> 94\n113 <-> 92\n115 <-> 98\n118 <-> 105\n122 <-> 104\n125 <-> 102\n127 <-> 108\n130 <-> 111\n134 <-> 114\n137 <-> 112\n140 <-> 115\n142 <-> 118\n145 <-> 121\n149 <-> 123\n152 <-> 124\n154 <-> 127\n157 <-> 130\n159 <-> 132\n160 <-> 133\n162 <-> 136\n164 <-> 139\nNot mapped:\n24\n27\n28\n37\n40\n43\n44\n53\n79\n92\n95\n108\n111\n120\n123\n132\n135\n138\n139\n147\n150\n"
  },
  {
    "path": "testData/results/TestSwitchWithDeconstructionsWithoutNestedJavac.dec",
    "content": "package pkg;\n\npublic class TestSwitchWithDeconstructionsWithoutNestedJavac {\n   public static void main(String[] args) {\n   }// 6\n\n   public static void testStringString(Object o) {\n      switch (o) {// 13\n         case R1(String var4, String s2):\n            if (s2.isEmpty()) {// 15\n               System.out.println(\"2\");// 16\n            }\n            break;\n         default:\n            System.out.println(\"3\");// 19\n            break;\n      }\n\n      System.out.println(\"1\");// 21\n   }\n\n   public static void testStringObjectWhen(Object o) {\n      switch (o) {// 25\n         case R1(String s1, Object var10) when s1.hashCode() == 3:// 26\n            if (s1.hashCode() == 1) {// 27\n               System.out.println(\"2\");// 28\n               System.out.println(\"2\");// 29\n               System.out.println(\"2\");// 30\n            }\n            break;\n         default:\n            System.out.println(\"3\");// 33\n            break;\n      }\n\n      System.out.println(\"1\");// 35\n   }\n\n   public static void testStringObject(Object o) {\n      label34:\n      switch (o) {// 39\n         case R1(String var4, Object var10) when var4.isEmpty():// 41\n            System.out.println(\"1\");// 42\n         default:\n            System.out.println(\"3\");// 45\n            break;\n      }\n\n      System.out.println(\"1\");// 47\n   }\n\n   public static void testObjectString(Object o) {\n      switch (o) {// 51\n         case R1(Object s1, String var5):\n            if (s1.hashCode() == 1) {// 53\n               System.out.println(\"1\");// 54\n            }\n            break;\n         default:\n            System.out.println(\"3\");// 57\n            break;\n      }\n\n      System.out.println(\"1\");// 59\n   }\n\n   public static void testObjectObject(Object o) {\n      switch (o) {// 63\n         case R1(Object s1, Object var10):\n            if (s1.hashCode() == 1) {// 65\n               System.out.println(\"1\");// 66\n            }\n            break;\n         default:\n            System.out.println(\"3\");// 69\n      }\n\n      System.out.println(\"1\");// 71\n   }\n\n   static record R1(Object o, Object o2) {\n      R1(Object o, Object o2) {\n         this.o = o;\n         this.o2 = o2;\n      }\n\n      public Object o() {\n         return this.o;\n      }\n\n      public Object o2() {\n         return this.o2;// 8\n      }\n   }\n}\n\nclass 'pkg/TestSwitchWithDeconstructionsWithoutNestedJavac' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      4\n   }\n\n   method 'testStringString (Ljava/lang/Object;)V' {\n      10      7\n      5b      9\n      5d      9\n      63      10\n      66      10\n      68      10\n      6e      14\n      71      14\n      73      14\n      76      15\n      79      18\n      7c      18\n      7e      18\n      81      19\n   }\n\n   method 'testStringObjectWhen (Ljava/lang/Object;)V' {\n      10      22\n      4a      23\n      4d      23\n      4e      23\n      58      24\n      5b      24\n      5c      24\n      5f      25\n      62      25\n      64      25\n      67      26\n      6a      26\n      6c      26\n      6f      27\n      72      27\n      74      27\n      7a      31\n      7d      31\n      7f      31\n      82      32\n      85      35\n      88      35\n      8a      35\n      8d      36\n   }\n\n   method 'testStringObject (Ljava/lang/Object;)V' {\n      10      40\n      50      41\n      58      42\n      5b      42\n      5d      42\n      63      44\n      66      44\n      68      44\n      6b      45\n      6e      48\n      71      48\n      73      48\n      76      49\n   }\n\n   method 'testObjectString (Ljava/lang/Object;)V' {\n      10      52\n      50      54\n      52      54\n      55      54\n      59      55\n      5c      55\n      5e      55\n      64      59\n      67      59\n      69      59\n      6c      60\n      6f      63\n      72      63\n      74      63\n      77      64\n   }\n\n   method 'testObjectObject (Ljava/lang/Object;)V' {\n      10      67\n      3f      69\n      42      69\n      43      69\n      46      70\n      49      70\n      4b      70\n      51      74\n      54      74\n      56      74\n      59      77\n      5c      77\n      5e      77\n      61      78\n   }\n}\n\nclass 'pkg/TestSwitchWithDeconstructionsWithoutNestedJavac$R1' {\n   method '<init> (Ljava/lang/Object;Ljava/lang/Object;)V' {\n      6      82\n      b      83\n      e      84\n   }\n\n   method 'o ()Ljava/lang/Object;' {\n      1      87\n      4      87\n   }\n\n   method 'o2 ()Ljava/lang/Object;' {\n      1      91\n      4      91\n   }\n}\n\nLines mapping:\n6 <-> 5\n8 <-> 92\n13 <-> 8\n15 <-> 10\n16 <-> 11\n19 <-> 15\n21 <-> 19\n25 <-> 23\n26 <-> 24\n27 <-> 25\n28 <-> 26\n29 <-> 27\n30 <-> 28\n33 <-> 32\n35 <-> 36\n39 <-> 41\n41 <-> 42\n42 <-> 43\n45 <-> 45\n47 <-> 49\n51 <-> 53\n53 <-> 55\n54 <-> 56\n57 <-> 60\n59 <-> 64\n63 <-> 68\n65 <-> 70\n66 <-> 71\n69 <-> 75\n71 <-> 78\nNot mapped:\n14\n22\n36\n40\n48\n52\n60\n64\n72\n"
  },
  {
    "path": "testData/results/TestSynchronizedMapping.dec",
    "content": "package pkg;\n\npublic class TestSynchronizedMapping {\n   public int test(int var1) {\n      synchronized(this) {// 8\n         ++var1;// 9\n      }// 10\n\n      return var1++;// 11\n   }\n\n   public void test2(String var1) {\n      System.out.println(var1);// 15\n   }// 16\n}\n\nclass 'pkg/TestSynchronizedMapping' {\n   method 'test (I)I' {\n      3      4\n      4      5\n      8      6\n      12      8\n      15      8\n   }\n\n   method 'test2 (Ljava/lang/String;)V' {\n      0      12\n      4      12\n      7      13\n   }\n}\n\nLines mapping:\n8 <-> 5\n9 <-> 6\n10 <-> 7\n11 <-> 9\n15 <-> 13\n16 <-> 14\n"
  },
  {
    "path": "testData/results/TestSynchronizedUnprotected.dec",
    "content": "package pkg;\n\nimport kotlin.Metadata;\nimport kotlin.Unit;\n\n@Metadata(\n   mv = {1, 1, 11},\n   bv = {1, 0, 2},\n   k = 1,\n   xi = 2,\n   d1 = {\"\\u0000\\u0010\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0002\\b\\u0002\\n\\u0002\\u0010\\u0002\\u0018\\u00002\\u00020\\u0001B\\u0005¢\\u0006\\u0002\\u0010\\u0002J\\u0006\\u0010\\u0003\\u001a\\u00020\\u0004\"},\n   d2 = {\"Lpkg/TestSynchronizedUnprotected;\", \"\", \"()V\", \"test\", \"\"}\n)\npublic final class TestSynchronizedUnprotected {\n   public final void test() {\n      synchronized(this) {\n         String var2 = \"Boom\";// 6\n         System.out.println(var2);\n         Unit var3 = Unit.INSTANCE;// 5 7\n      }\n   }\n}\n\nclass 'pkg/TestSynchronizedUnprotected' {\n   method 'test ()V' {\n      3      15\n      5      16\n      7      16\n      8      17\n      c      17\n      f      18\n      12      18\n      15      20\n   }\n}\n\nLines mapping:\n5 <-> 19\n6 <-> 17\n7 <-> 19\nNot mapped:\n8\n"
  },
  {
    "path": "testData/results/TestSyntheticAccess.dec",
    "content": "package pkg;\n\nclass TestSyntheticAccess {\n   private static int s;\n   private int i;\n\n   private class Assigner {\n      void assignI(int var1) {\n         TestSyntheticAccess.this.i = var1;// 32\n      }// 33\n\n      void assignS(int var1) {\n         TestSyntheticAccess.s = var1;// 36\n      }// 37\n   }\n\n   private class Incrementer {\n      void orI() {\n         TestSyntheticAccess.this.i = TestSyntheticAccess.this.i | 1;// 10\n      }// 11\n\n      void incrementI() {\n         TestSyntheticAccess.this.i++;// 14\n      }// 15\n\n      void decrementI() {\n         --TestSyntheticAccess.this.i;// 18\n      }// 19\n\n      void incrementS() {\n         ++TestSyntheticAccess.s;// 22\n      }// 23\n\n      void decrementS() {\n         TestSyntheticAccess.s--;// 26\n      }// 27\n   }\n}\n\nclass 'pkg/TestSyntheticAccess$Assigner' {\n   method 'assignI (I)V' {\n      5      8\n      9      9\n   }\n\n   method 'assignS (I)V' {\n      1      12\n      5      13\n   }\n}\n\nclass 'pkg/TestSyntheticAccess$Incrementer' {\n   method 'orI ()V' {\n      8      18\n      b      18\n      d      18\n      11      19\n   }\n\n   method 'incrementI ()V' {\n      2      22\n      4      22\n      8      23\n   }\n\n   method 'decrementI ()V' {\n      2      26\n      4      26\n      8      27\n   }\n\n   method 'incrementS ()V' {\n      0      30\n      4      31\n   }\n\n   method 'decrementS ()V' {\n      0      34\n      4      35\n   }\n}\n\nLines mapping:\n10 <-> 19\n11 <-> 20\n14 <-> 23\n15 <-> 24\n18 <-> 27\n19 <-> 28\n22 <-> 31\n23 <-> 32\n26 <-> 35\n27 <-> 36\n32 <-> 9\n33 <-> 10\n36 <-> 13\n37 <-> 14\n"
  },
  {
    "path": "testData/results/TestThrowException.dec",
    "content": "package pkg;\n\npublic class TestThrowException {\n   Runnable r;\n\n   public TestThrowException(int var1) {\n      if (var1 > 0) {// 9\n         throw new IllegalArgumentException(\"xxx\");// 10\n      } else {\n         this.r = new Runnable() {// 12\n            public void run() {\n               boolean var1 = true;// 15\n            }// 16\n         };\n      }\n   }// 18\n}\n\nclass 'pkg/TestThrowException$1' {\n   method 'run ()V' {\n      0      11\n      1      11\n      2      12\n   }\n}\n\nclass 'pkg/TestThrowException' {\n   method '<init> (I)V' {\n      5      6\n      c      7\n      11      7\n      1b      9\n      1e      15\n   }\n}\n\nLines mapping:\n9 <-> 7\n10 <-> 8\n12 <-> 10\n15 <-> 12\n16 <-> 13\n18 <-> 16\nNot mapped:\n8\n"
  },
  {
    "path": "testData/results/TestTryCatchFinally.dec",
    "content": "package pkg;\n\npublic class TestTryCatchFinally {\n   public void test1(String var1) {\n      try {\n         System.out.println(\"sout1\");// 24\n      } catch (Exception var9) {\n         try {\n            System.out.println(\"sout2\");// 27\n         } catch (Exception var8) {// 28\n         }\n      } finally {\n         System.out.println(\"finally\");// 34\n      }\n\n   }// 36\n\n   int foo(int var1) throws Exception {\n      if (var1 < 1) {// 39\n         throw new RuntimeException();// 40\n      } else if (var1 < 5) {// 41\n         return var1;// 42\n      } else {\n         throw new Exception();// 45\n      }\n   }\n\n   public int test(String var1) {\n      try {\n         int var2 = Integer.parseInt(var1);// 51\n         return var2;\n      } catch (Exception var6) {// 52\n         System.out.println(\"Error\" + var6);// 53\n      } finally {\n         System.out.println(\"Finally\");// 55\n      }\n\n      return -1;// 56 57\n   }\n}\n\nclass 'pkg/TestTryCatchFinally' {\n   method 'test1 (Ljava/lang/String;)V' {\n      0      5\n      3      5\n      5      5\n      14      8\n      17      8\n      19      8\n      1f      9\n      2b      12\n      2d      12\n      30      12\n      38      15\n   }\n\n   method 'foo (I)I' {\n      1      18\n      2      18\n      c      19\n      e      20\n      f      20\n      13      21\n      1b      23\n   }\n\n   method 'test (Ljava/lang/String;)I' {\n      1      29\n      4      29\n      e      30\n      f      31\n      10      32\n      1a      32\n      23      32\n      26      32\n      31      37\n      34      34\n      35      34\n      38      34\n      3f      37\n   }\n}\n\nLines mapping:\n24 <-> 6\n27 <-> 9\n28 <-> 10\n34 <-> 13\n36 <-> 16\n39 <-> 19\n40 <-> 20\n41 <-> 21\n42 <-> 22\n45 <-> 24\n51 <-> 30\n52 <-> 32\n53 <-> 33\n55 <-> 35\n56 <-> 38\n57 <-> 38\nNot mapped:\n25\n32\n35\n"
  },
  {
    "path": "testData/results/TestTryCatchFinallyJsrRet.dec",
    "content": "package pkg;\n\npublic class TestTryCatchFinallyJsrRet {\n   public void test1(String var1) {\n      try {\n         System.out.println(\"sout1\");// 36\n      } catch (Exception var10) {\n         try {\n            System.out.println(\"sout2\");// 39\n         } catch (Exception var9) {// 40\n         }\n      } finally {\n         System.out.println(\"finally\");// 46\n      }\n\n   }// 48\n\n   int foo(int var1) throws Exception {\n      if (var1 < 1) {// 51\n         throw new RuntimeException();// 52\n      } else if (var1 < 5) {// 53\n         return var1;// 54\n      } else {\n         throw new Exception();// 57\n      }\n   }\n\n   public int test(String var1) {\n      try {\n         int var2 = Integer.parseInt(var1);// 63\n         return var2;\n      } catch (Exception var7) {// 65\n         System.out.println(\"Error\" + var7);\n      } finally {\n         System.out.println(\"Finally\");// 67\n      }\n\n      return -1;\n   }\n}\n\nclass 'pkg/TestTryCatchFinallyJsrRet' {\n   method 'test1 (Ljava/lang/String;)V' {\n      0      5\n      3      5\n      5      5\n      f      8\n      12      8\n      14      8\n      1a      9\n      21      12\n      23      12\n      29      12\n      35      15\n   }\n\n   method 'foo (I)I' {\n      1      18\n      2      18\n      c      19\n      e      20\n      f      20\n      13      21\n      1b      23\n   }\n\n   method 'test (Ljava/lang/String;)I' {\n      1      29\n      4      29\n      a      31\n      b      32\n      15      32\n      1e      32\n      21      32\n      2a      34\n      2b      34\n      30      34\n      37      37\n      3a      30\n   }\n}\n\nLines mapping:\n36 <-> 6\n39 <-> 9\n40 <-> 10\n46 <-> 13\n48 <-> 16\n51 <-> 19\n52 <-> 20\n53 <-> 21\n54 <-> 22\n57 <-> 24\n63 <-> 30\n65 <-> 32\n67 <-> 35\nNot mapped:\n37\n38\n45\n66\n69\n"
  },
  {
    "path": "testData/results/TestUnionType.dec",
    "content": "package pkg;\n\nimport java.io.Serializable;\nimport java.util.Comparator;\n\npublic interface TestUnionType {\n   static Comparator comparingInt() {\n      return (Comparator & Serializable)((c1, c2) -> {// 8\n         return 1;\n      }));\n   }\n}\n\nclass 'pkg/TestUnionType' {\n   method 'lambda$comparingInt$ff46620a$1 (Ljava/lang/Object;Ljava/lang/Object;)I' {\n      0      8\n      1      8\n   }\n\n   method 'comparingInt ()Ljava/util/Comparator;' {\n      5      7\n      8      7\n      b      7\n   }\n}\n\nLines mapping:\n8 <-> 8\n"
  },
  {
    "path": "testData/results/TestUnsupportedConstantPoolEntry.dec",
    "content": ""
  },
  {
    "path": "testData/results/TestVarArgCalls.dec",
    "content": "package pkg;\n\npublic class TestVarArgCalls {\n   public void doSmth() {\n      this.printAll(\"Test\");// 5\n      this.printAll(\"Test: %s\", \"abc\");// 6\n      this.printAll(\"Test: %s - %s\", \"abc\", \"DEF\");// 7\n      this.printComplex(\"Test\");// 9\n      this.printComplex(\"Test: %[0]s\", new String[]{\"abc\"});// 10\n      this.printComplex(\"Test: %[0]s - %[0]s\", new String[]{\"abc\"}, new String[]{\"DEF\"});// 11\n      String.format(\"Test\");// 13\n      String.format(\"Test: %d\", 123);// 14\n      String.format(\"Test: %d - %s\", 123, \"DEF\");// 15\n      Object[] data = new Object[]{\"Hello\"};// 17\n      String.format(\"Test: %s\", (Object)data);// 18\n      String.format(\"Test: %s\", (Object[])data);// 19\n   }// 20\n\n   public void printAll(String fmt, String... params) {\n      System.out.println(String.format(fmt, (Object[])params));// 23\n   }// 24\n\n   public void printComplex(String fmt, String[]... params) {\n      System.out.println(String.format(fmt, (Object[])params));// 27\n   }// 28\n}\n\nclass 'pkg/TestVarArgCalls' {\n   method 'doSmth ()V' {\n      1      4\n      7      4\n      b      5\n      13      5\n      16      5\n      1a      6\n      22      6\n      27      6\n      2a      6\n      2e      7\n      34      7\n      38      8\n      46      8\n      4a      8\n      4e      9\n      5c      9\n      68      9\n      6c      9\n      6f      10\n      75      10\n      79      11\n      81      11\n      83      11\n      87      11\n      8b      12\n      93      12\n      95      12\n      9b      12\n      9e      12\n      a8      13\n      ab      13\n      ac      14\n      b6      14\n      ba      15\n      bd      15\n      c0      15\n      c4      16\n   }\n\n   method 'printAll (Ljava/lang/String;[Ljava/lang/String;)V' {\n      0      19\n      5      19\n      8      19\n      b      19\n      e      20\n   }\n\n   method 'printComplex (Ljava/lang/String;[[Ljava/lang/String;)V' {\n      0      23\n      5      23\n      8      23\n      b      23\n      e      24\n   }\n}\n\nLines mapping:\n5 <-> 5\n6 <-> 6\n7 <-> 7\n9 <-> 8\n10 <-> 9\n11 <-> 10\n13 <-> 11\n14 <-> 12\n15 <-> 13\n17 <-> 14\n18 <-> 15\n19 <-> 16\n20 <-> 17\n23 <-> 20\n24 <-> 21\n27 <-> 24\n28 <-> 25\n"
  },
  {
    "path": "testData/results/TryToPreserveCast.dec",
    "content": "import java.nio.Buffer;\nimport java.nio.ByteBuffer;\n\npublic class TryToPreserveCast {\n   public static void main(String[] args) {\n   }// 10\n\n   public void test(ByteBuffer buffer) {\n      ((Buffer)buffer).limit(1);// 13\n      buffer.limit(2);// 14\n   }// 15\n}\n\nclass 'TryToPreserveCast' {\n   method 'main ([Ljava/lang/String;)V' {\n      0      5\n   }\n\n   method 'test (Ljava/nio/ByteBuffer;)V' {\n      1      8\n      2      8\n      7      9\n      8      9\n      c      10\n   }\n}\n\nLines mapping:\n10 <-> 6\n13 <-> 9\n14 <-> 10\n15 <-> 11\n"
  },
  {
    "path": "testData/results/module-info.dec",
    "content": "import sample.pkg1.TestModuleAnno;\n\n@TestModuleAnno(\"...\")\nmodule sample.module {\n   requires java.desktop;\n\n   exports sample.pkg1;\n   exports sample.pkg2 to\n      java.base;\n\n   opens sample.pkg1;\n   opens sample.pkg2 to\n      java.base;\n\n   uses java.util.spi.ToolProvider;\n\n   provides sample.pkg1.TestService with\n      sample.pkg1.TestServiceImpl;\n}\n"
  },
  {
    "path": "testData/results/package-info.dec",
    "content": "@PkgAnno(\"...\")\npackage pkg;\n\nimport ext.PkgAnno;\n"
  },
  {
    "path": "testData/src/ext/PkgAnno.java",
    "content": "package ext;\n\nimport java.lang.annotation.*;\n\n@Target(ElementType.PACKAGE)\npublic @interface PkgAnno {\n  String value();\n}\n"
  },
  {
    "path": "testData/src/ext/Shadow.java",
    "content": "package ext;\n\n// companion class for pkg/TestShadowing.java\npublic class Shadow {\n  public static class B { }\n}"
  },
  {
    "path": "testData/src/ext/TestClashNameIface.java",
    "content": "package ext;\ninterface TestClashNameIface {\n  public void foo();\n}\n"
  },
  {
    "path": "testData/src/ext/TestClashNameParent.java",
    "content": "package ext;\n\npublic class TestClashNameParent {\n  int SharedName3 = 0;\n}\n"
  },
  {
    "path": "testData/src/java11/TestDynamicConstantPoolEntry.jcoder",
    "content": "/**\n *\n *  This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n *  using <code>asmtools jcoder *.jcoder</code> command line.\n *\n *  Test reading constant pool with CONSTANT_Dynamic entry at index #2. Dynamic constant didn't used in the bytecode and thus \n *  should not affect decompiled code correctness in any way.\n *\n */\nclass java11/TestDynamicConstantPoolEntry {\n  0xCAFEBABE;\n  0; // minor version\n  55; // version\n  [] { // Constant Pool\n    ; // first element is empty\n    String #29; // #1\n    Dynamic 0s #25; // #2\n    Field #35 #30; // #3\n    Method #32 #36; // #4\n    Method #22 #14; // #5\n    Utf8 \"TestDynamicConstantPoolEntry.jcoder\"; // #6\n    Utf8 \"()V\"; // #7\n    Method #10 #9; // #8\n    NameAndType #27 #13; // #9\n    class #23; // #10\n    Utf8 \"Ljava/lang/Object;\"; // #11\n    Utf8 \"<init>\"; // #12\n    Utf8 \"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;\"; // #13\n    NameAndType #12 #7; // #14\n    Utf8 \"out\"; // #15\n    Utf8 \"java/io/PrintStream\"; // #16\n    MethodHandle 6b #8; // #17\n    Utf8 \"SourceFile\"; // #18\n    class #31; // #19\n    Utf8 \"java/lang/System\"; // #20\n    Utf8 \"(Ljava/lang/String;)V\"; // #21\n    class #34; // #22\n    Utf8 \"java/lang/invoke/ConstantBootstraps\"; // #23\n    Utf8 \"println\"; // #24\n    NameAndType #27 #11; // #25\n    Utf8 \"Ljava/io/PrintStream;\"; // #26\n    Utf8 \"nullConstant\"; // #27\n    Utf8 \"BootstrapMethods\"; // #28\n    Utf8 \"This class file contains constant pool entry of type CONSTANT_Dynamic, not used in the bytecode.\"; // #29\n    NameAndType #15 #26; // #30\n    Utf8 \"java11/TestDynamicConstantPoolEntry\"; // #31\n    class #16; // #32\n    Utf8 \"main\"; // #33\n    Utf8 \"java/lang/Object\"; // #34\n    class #20; // #35\n    NameAndType #24 #21; // #36\n    Utf8 \"([Ljava/lang/String;)V\"; // #37\n    Utf8 \"Code\"; // #38\n  } // Constant Pool\n\n  0x0021; // access\n  #19;// this_cpx\n  #22;// super_cpx\n\n  [] { // Interfaces\n  } // Interfaces\n\n  [] { // Fields\n  } // Fields\n\n  [] { // Methods\n    {  // method\n      0x0001; // access\n      #12; // name_index\n      #7; // descriptor_index\n      [] { // Attributes\n        Attr(#38) { // Code\n          1; // max_stack\n          1; // max_locals\n          Bytes[]{\n            0x2AB70005B1;\n          }\n          [] { // Traps\n          } // end Traps\n          [] { // Attributes\n          } // Attributes\n        } // end Code\n      } // Attributes\n    }\n    ;\n    {  // method\n      0x0089; // access\n      #33; // name_index\n      #37; // descriptor_index\n      [] { // Attributes\n        Attr(#38) { // Code\n          2; // max_stack\n          1; // max_locals\n          Bytes[]{\n            0xB200031201B60004;\n            0xB1;\n          }\n          [] { // Traps\n          } // end Traps\n          [] { // Attributes\n          } // Attributes\n        } // end Code\n      } // Attributes\n    }\n  } // Methods\n\n  [] { // Attributes\n    Attr(#18) { // SourceFile\n      #6;\n    } // end SourceFile\n    ;\n    Attr(#28) { // BootstrapMethods\n      [] { // bootstrap_methods\n        {  //  bootstrap_method\n          #17; // bootstrap_method_ref\n          [] { // bootstrap_arguments\n          }  //  bootstrap_arguments\n        }  //  bootstrap_method\n      }\n    } // end BootstrapMethods\n  } // Attributes\n} // end class java11/TestDynamicConstantPoolEntry"
  },
  {
    "path": "testData/src/java11/TestJava11StringConcat.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage java11;\n\npublic class TestJava11StringConcat {\n  public String test1(String prefix, int a) {\n    return prefix + a;\n  }\n\n  public String test2(String var, int b, Object c) {\n    return \"(\" + var + \"-\" + b + \"---\" + c + \")\";\n  }\n}"
  },
  {
    "path": "testData/src/java11/TestJava11StringConcatEmptyAffix.java",
    "content": "package java11;\n\npublic class TestJava11StringConcatEmptyAffix {\n\n    public String testEmptyPrefixInt(int value) {\n        return \"\" + value;\n    }\n\n    public String testEmptyPrefixString(String value) {\n        return \"\" + value;\n    }\n\n    public String testPrefixInt(int value) {\n        return \"prefix\" + value;\n    }\n\n    public String testPrefixString(String value) {\n        return \"prefix\" + value;\n    }\n\n    // NOTE: Empty suffix is indistinguishable from empty prefix in bytecode. Will be decompiled as latter.\n    public String testIntEmptySuffix(int value) {\n        return value + \"\";\n    }\n\n    // NOTE: Empty suffix is indistinguishable from empty prefix in bytecode. Will be decompiled as latter.\n    public String testStringEmptySuffix(String value) {\n        return value + \"\";\n    }\n\n    public String testIntSuffix(int value) {\n        return value + \"suffix\";\n    }\n\n    public String testStringSuffix(String value) {\n        return value + \"suffix\";\n    }\n\n    public String testIntInt(int first, int second) {\n        return \"\" + first + second;\n    }\n\n    public String testIntIntSuffix(int first, int second) {\n        return \"\" + first + second + \"suffix\";\n    }\n\n    public String testIntString(int intValue, String stringValue) {\n        return \"\" + intValue + stringValue;\n    }\n\n    public String testStringInt(int intValue, String stringValue) {\n        return stringValue + intValue;\n    }\n\n}"
  },
  {
    "path": "testData/src/java11/TestJava11StringConcatSpecialChars.java",
    "content": "package java11;\n\npublic class TestJava11StringConcatSpecialChars {\n\n    public String testOrdinaryInfix(String first, String second, String last) {\n        return \"BEGIN \" + first + \" (first infix) \" + second + \" (second infix) \" + last + \" END\";\n    }\n\n    public String testSpecialCharsInfix(String first, String second, String last) {\n        return \"BEGIN \" + first + \" (first\\u0001infix) \" + second + \" (second\\u0002infix) \" + last + \" END\";\n    }\n\n}"
  },
  {
    "path": "testData/src/java11/TestUnsupportedConstantPoolEntry.jcoder",
    "content": "/**\n *\n *  This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n *  using <code>asmtools jcoder *.jcoder</code> command line.\n *\n *  Test handling constant pool entries of unknown type.\n *\n *  Constant pool entry at index #10 has unknown tag 0xFF, not defined in JVMS.\n *  We should refuse to process this *.class file as we has no chance to correctly detect constant pool entry size\n *  and process rest of the class.\n *\n */\nclass java11/TestUnsupportedConstantPoolEntry {\n  0xCAFEBABE;\n  0; // minor version\n  55; // version\n  [] { // Constant Pool\n    ; // first element is empty\n    Method #3 #7; // #1\n    class #8; // #2\n    class #9; // #3\n    Utf8 \"<init>\"; // #4\n    Utf8 \"()V\"; // #5\n    Utf8 \"Code\"; // #6\n    NameAndType #4 #5; // #7\n    Utf8 \"java11/TestUnsupportedConstantPoolEntry\"; // #8\n    Utf8 \"java/lang/Object\"; // #9\n    0xFF #3; // #10\n  } // Constant Pool\n\n  0x0021; // access\n  #2;// this_cpx\n  #3;// super_cpx\n\n  [] { // Interfaces\n  } // Interfaces\n\n  [] { // Fields\n  } // Fields\n\n  [] { // Methods\n    {  // method\n      0x0001; // access\n      #4; // name_index\n      #5; // descriptor_index\n      [] { // Attributes\n        Attr(#6) { // Code\n          1; // max_stack\n          1; // max_locals\n          Bytes[]{\n            0x2AB70001B1;\n          }\n          [] { // Traps\n          } // end Traps\n          [] { // Attributes\n          } // Attributes\n        } // end Code\n      } // Attributes\n    }\n  } // Methods\n\n  [] { // Attributes\n  } // Attributes\n} // end class java11/TestUnsupportedConstantPoolEntry"
  },
  {
    "path": "testData/src/java9/TestJava9PrivateInterfaceMethod.java",
    "content": "package java9;\n\npublic interface TestJava9PrivateInterfaceMethod {\n  private void privateMethod() {}\n}"
  },
  {
    "path": "testData/src/java9/TestJava9StringConcat.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage java9;\n\npublic class TestJava9StringConcat {\n  public String test1(String prefix, int a) {\n    return prefix + a;\n  }\n\n  public String test2(String var, int b, Object c) {\n    return \"(\" + var + \"-\" + b + \"---\" + c + \")\";\n  }\n}"
  },
  {
    "path": "testData/src/java9/sample.module/TestClass.java",
    "content": "package sample.pkg2;\n\npublic class TestClass {}"
  },
  {
    "path": "testData/src/java9/sample.module/TestModuleAnno.java",
    "content": "package sample.pkg1;\n\nimport java.lang.annotation.*;\n\n@Target(ElementType.MODULE)\npublic @interface TestModuleAnno {\n  String value();\n}\n"
  },
  {
    "path": "testData/src/java9/sample.module/TestService.java",
    "content": "package sample.pkg1;\n\npublic interface TestService {}"
  },
  {
    "path": "testData/src/java9/sample.module/TestServiceImpl.java",
    "content": "package sample.pkg1;\n\npublic class TestServiceImpl implements TestService {}"
  },
  {
    "path": "testData/src/java9/sample.module/module-info.java",
    "content": "import sample.pkg1.TestModuleAnno;\n\n@TestModuleAnno(\"...\")\nmodule sample.module {\n  requires java.desktop;\n\n  uses java.util.spi.ToolProvider;\n\n  provides sample.pkg1.TestService with sample.pkg1.TestServiceImpl;\n\n  exports sample.pkg1;\n\n  exports sample.pkg2 to java.base;\n\n  opens sample.pkg1;\n\n  opens sample.pkg2 to java.base;\n}\n"
  },
  {
    "path": "testData/src/patterns/TestInstanceofPatternNotSupported.java",
    "content": "package decompiler;\n\npublic class TestInstanceofPatternNotSupported {\n    void typePattern(Object str) {\n        if (!(str instanceof String)) {\n            System.out.println(\"no\");\n            return;\n        }\n        String s = (String) str;\n        if (s.length() > 3) {\n            System.out.println(s);\n        } else if (s.startsWith(\"a\")) {\n            System.out.println(s + \"\");\n        }\n    }\n}\n"
  },
  {
    "path": "testData/src/patterns/TestInstanceofWithPattern.java",
    "content": "package patterns;\n\nimport java.util.Collection;\nimport java.util.List;\n\npublic class TestInstanceofWithPattern {\n\n  void typePattern1(Object str) {\n    if (str instanceof String s) {\n      System.out.println(s);\n    } else {\n      System.out.println(\"no\");\n    }\n  }\n\n  void typePattern2(Object str) {\n    if (!(str instanceof String)) {\n      System.out.println(\"no\");\n      return;\n    }\n    String s = (String) str;\n    if (s.length() > 3) {\n      System.out.println(s);\n    } else if (s.startsWith(\"a\")) {\n      System.out.println(s + \"\");\n    }\n  }\n\n  void typePatternInBinaryExpr(Object str) {\n    if (str instanceof String s && (s.length() > 1 || s.startsWith(\"a\"))) {\n      System.out.println(s);\n    } else {\n      System.out.println(\"no\");\n    }\n  }\n\n  String returnInstanceof(Object obj) {\n    if (obj instanceof String s && s.length() > 50) {\n      return '\"' + s.substring(0, 50) + \"...\\\"\";\n    }\n    if (obj instanceof String s) {\n      return '\"' + s + '\"';\n    }\n    if (obj instanceof Collection<?> c) {\n      return \"Collection (size = \" + c.size() + \")\";\n    }\n    return obj.toString();\n  }\n\n  String complex(Object obj1, Object obj2) {\n    while(true) {\n      try {\n        if (obj1 instanceof String s) {\n          while (true) {\n            if (s.startsWith(\"a\")) {\n              return s;\n            }\n          }\n        } else if (obj2 instanceof Collection<?> c) {\n          return c.toString();\n        }\n      } catch (Exception e) {\n        if (obj2 instanceof String s) {\n          while (true) {\n            if (s.startsWith(\"b\")) {\n              return s + \"b\";\n            }\n          }\n        } else if (obj2 instanceof List<?> l) {\n          return getStr() + l.size();\n        }\n      }\n    }\n  }\n\n  String getStr() {\n    return null;\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/MoreAnnotations.java",
    "content": "package pkg;\n\npublic @interface MoreAnnotations {\n\n  int intValue() default 1;\n  byte byteValue() default 1;\n  float floatValue() default Float.POSITIVE_INFINITY;\n  double doubleValue() default Double.NaN;\n  boolean booleanValue() default true;\n  short shortValue() default 1;\n  long longValue() default 1;\n  char charValue() default '0';\n  TestEnum enumValue() default TestEnum.FirstValue;\n  NestedAnnotation annotationValue() default @NestedAnnotation;\n  String stringValue() default \"default\";\n  Class<? extends CharSequence> classValue() default CharSequence.class;\n\n  int[] intArray() default { 1, 0, Integer.MAX_VALUE, Integer.MIN_VALUE };\n  byte[] byteArray() default { 1, 0, Byte.MAX_VALUE, Byte.MIN_VALUE, (byte)0xFF };\n  float[] floatArray() default { 1, 0, Float.MAX_VALUE, Float.MIN_VALUE, Float.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY };\n  double[] doubleArray() default {  1, 0, Double.MAX_VALUE, Double.MIN_VALUE, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY };\n  boolean[] booleanArray() default { true, false };\n  short[] shortArray() default { 1, 0, Short.MAX_VALUE, Short.MIN_VALUE, (short)0xFFFF };\n  long[] longArray() default { 1, 0, Long.MAX_VALUE, Long.MIN_VALUE };\n  char[] charArray() default { 1, 0, Character.MAX_VALUE, Character.MIN_VALUE };\n  TestEnum[] enumArray() default { TestEnum.FirstValue };\n  NestedAnnotation[] annotationArray() default { @NestedAnnotation };\n  String[] stringArray() default { \"first\", \"second\", \"\" };\n  Class<? extends CharSequence>[] classArray() default { CharSequence.class, String.class, StringBuilder.class };\n\n  @interface NestedAnnotation {\n    String value() default \"MyString\";\n  }\n\n  @MoreAnnotations(\n    intValue = 1,\n    byteValue = 1,\n    floatValue = 1,\n    doubleValue = 1,\n    booleanValue = true,\n    shortValue = 1,\n    longValue = 1,\n    charValue = '\\n',\n    enumValue = TestEnum.FirstValue,\n    annotationValue = @NestedAnnotation(\"a\"),\n    stringValue = \"\",\n    classValue = String.class\n  )\n  String annotatedWithValues = \"\";\n\n  @MoreAnnotations(\n    intArray = {},\n    byteArray = {},\n    floatArray = {},\n    doubleArray = {},\n    booleanArray = {},\n    shortArray = {},\n    longArray = {},\n    charArray = {},\n    enumArray = {},\n    annotationArray = {},\n    stringArray =  {},\n    classArray = {}\n  )\n  String annotatedWithEmptyArrays = \"\";\n\n  @MoreAnnotations(\n    intArray = { 1, 0, Integer.MAX_VALUE, Integer.MIN_VALUE },\n    byteArray = { 1, 0, Byte.MAX_VALUE, Byte.MIN_VALUE, (byte)0xFF },\n    floatArray = { 1, 0, Float.MAX_VALUE, Float.MIN_VALUE, Float.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY },\n    doubleArray = {  1, 0, Double.MAX_VALUE, Double.MIN_VALUE, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY },\n    booleanArray = { true, false },\n    shortArray = { 1, 0, Short.MAX_VALUE, Short.MIN_VALUE, (short)0xFFFF },\n    longArray = { 1, 0, Long.MAX_VALUE, Long.MIN_VALUE },\n    charArray = { 'a', '\\n', 1, 0, Character.MAX_VALUE, Character.MIN_VALUE },\n    enumArray = { TestEnum.FirstValue , TestEnum.SecondValue },\n    annotationArray = { @NestedAnnotation(\"a\"), @NestedAnnotation(\"b\") },\n    stringArray = { \"first\", \"second\", \"\" },\n    classArray = { CharSequence.class, String.class, StringBuilder.class }\n  )\n  String annotatedWithArrays = \"\";\n\n  public enum TestEnum {\n\n    FirstValue,\n    SecondValue\n\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/NestedType.java",
    "content": "package pkg;\n\npublic class NestedType {\n    public void doSomething() {\n        Foo foo = new Foo();\n        Foo.Bar bar = new Foo.Bar();\n        System.out.println(foo);\n        System.out.println(bar);\n\n        FooBar<String> fooBar = new FooBar<> ();\n        System.out.println(foo);\n    }\n\n    static class Foo {\n        public  void doSomething() {\n            Bar fooBar = new Bar();\n            FooBar.Bar<String, String> fooBarBar = new FooBar.Bar<>();\n            System.out.println(fooBar);\n            System.out.println(fooBarBar);\n        }\n\n        static class Bar { }\n    }\n\n    static class FooBar<T> {\n        public  void doSomething() {\n            Foo.Bar fooBar = new Foo.Bar();\n            Bar<String, String> fooBarBar = new Bar<>();\n            System.out.println(fooBar);\n            System.out.println(fooBarBar);\n        }\n\n        static class Bar<T, E> { }\n    }\n}"
  },
  {
    "path": "testData/src/pkg/Shadow.java",
    "content": "package pkg;\n\n// companion class for pkg/TestShadowing.java\npublic class Shadow { }"
  },
  {
    "path": "testData/src/pkg/SwitchOnStatic.java",
    "content": "public class SwitchOnStatic {\n  public static void main(String[] args) {\n    staticStringSelector();\n    staticIntSelector();\n    staticIntSelectorNotInlined();\n  }\n\n  public static void staticStringSelector() {\n    switch (getStaticStringSelector()) {\n      case \"1\":\n        System.out.println(\"a\");\n        break;\n      case \"2\":\n        System.out.println(\"b\");\n        break;\n    }\n  }\n\n  public static String getStaticStringSelector() {\n    return \"1\";\n  }\n\n  public static void staticIntSelector() {\n    switch (getStaticIntSelector()) {\n      case 1:\n        System.out.println(\"a\");\n        break;\n      case 2:\n        System.out.println(\"b\");\n        break;\n    }\n  }\n\n  public static int getStaticIntSelector() {\n    return 1;\n  }\n\n  public static void staticIntSelectorNotInlined() {\n    int cc = getStaticIntSelector();\n    switch (cc) {\n      case 1:\n        System.out.println(\"a\");\n        break;\n      case 2:\n        System.out.println(\"b\");\n        break;\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestAbstractMethods.java",
    "content": "package pkg;\n\nimport java.lang.Override;\nimport java.lang.Runnable;\n\npublic abstract class TestAbstractMethods {\n\n  public abstract void foo();\n\n  public int test(int a) {\n    return a;\n  }\n\n  protected abstract void foo1();\n\n  public void test2(String a) {\n    System.out.println(a);\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestAccessReplace.java",
    "content": "/*\n * Copyright 2000-2017 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestAccessReplace {\n  private static void fooS() {};\n  private void foo() {};\n  private static void fooSParams(long a, long b) {};\n  private void fooParams(long a, long b) {};\n\n  public class Inner {\n    public Inner(String b) {\n      fooS();\n      foo();\n      fooSParams(1,2);\n      fooParams(1,2);\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestAmbiguousCall.java",
    "content": "package pkg;\n\nclass TestAmbiguousCall {\n  void m1(RuntimeException e, String s) { }\n  void m1(IllegalArgumentException e, String s) { }\n\n  void test() {\n    IllegalArgumentException iae = new IllegalArgumentException();\n    m1((RuntimeException)iae, \"RE\");\n    m1(iae, \"IAE\");\n\n    RuntimeException re = new IllegalArgumentException();\n    m1(re, \"RE\");\n    m1((IllegalArgumentException)re, \"IAE\");\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestAnonymousClass.java",
    "content": "package pkg;\n\nimport java.lang.Exception;\nimport java.lang.Override;\nimport java.lang.Runnable;\n\npublic abstract class TestAnonymousClass {\n  void foo(int i)\n    throws Exception {\n    if (i > 0) {\n      I r = new I() {\n        public void foo() throws Exception {\n          int a = 5;\n          int b = 5;\n        }\n      };\n      r.foo();\n    }\n    else {\n      final int x =5;\n      System.out.println(x);\n    }\n  }\n\n  public static final Runnable R3 = new Runnable() {\n    @Override\n    public void run() {\n      int a =5;\n      int b =5;\n    }\n  };\n\n\n  void boo() {\n    int a =5;\n  }\n\n  void zoo() {\n    int a =5;\n  }\n\n  public static final Runnable R = new Runnable() {\n    @Override\n    public void run() {\n      int a =5;\n      int b =5;\n    }\n  };\n\n  public static final Runnable R1 = new Runnable() {\n    @Override\n    public void run() {\n      int a =5;\n      int b =5;\n    }\n  };\n\n  interface I {\n    void foo() throws Exception;\n  }\n\n  private static class Inner {\n    private static Runnable R_I = new Runnable() {\n      @Override\n      public void run() {\n        int a =5;\n        int b =5;\n      }\n    };\n  }\n\n  private final InnerRecursive y = new InnerRecursive(new InnerRecursive(null) {\n    @Override\n    void foo() {\n      int a =5;\n      int b =5;\n      int g =5;\n    }\n  }) {\n    int v =5;\n    int t =5;\n    int j =5;\n    int o =5;\n  };\n\n\n  private final InnerRecursive x = new InnerRecursive(new InnerRecursive(null) {\n    @Override\n    void foo() {\n      int a =5;\n      int b =5;\n      int g =5;\n    }\n  }) {\n    int v =5;\n    int t =5;\n    int j =5;\n    int o =5;\n  };\n\n  static class InnerRecursive {\n    InnerRecursive r;\n\n    public InnerRecursive(InnerRecursive r) {\n      this.r = r;\n    }\n\n    void foo() {\n\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestAnonymousClassConstructor.java",
    "content": "package pkg;\n\nclass TestAnonymousClassConstructor {\n  void innerPrivateString() {\n    new InnerPrivateString(\"text\"){};\n  }\n\n  void innerPrivate() {\n    new InnerPrivate(3, 4){};\n  }\n\n  void innerStaticPrivateString() {\n    new InnerStaticPrivateString(\"text\"){};\n  }\n\n  void innerStaticPrivate() {\n    new InnerStaticPrivate(3, 4){};\n  }\n\n  static void innerStaticPrivateStringStatic() {\n    new InnerStaticPrivateString(\"text\"){};\n  }\n\n  static void innerStaticPrivateStatic() {\n    new InnerStaticPrivate(3, 4){};\n  }\n\n  void innerPublicString() {\n    new InnerPublicString(\"text\"){};\n  }\n\n  void innerPublic() {\n    new InnerPublic(3, 4){};\n  }\n\n  void innerStaticPublicString() {\n    new InnerStaticPublicString(\"text\"){};\n  }\n\n  void innerStaticPublic() {\n    new InnerStaticPublic(3, 4){};\n  }\n\n  static void innerStaticPublicStringStatic() {\n    new InnerStaticPublicString(\"text\"){};\n  }\n\n  static void innerStaticPublicStatic() {\n    new InnerStaticPublic(3, 4){};\n  }\n\n  static void n(String s) {\n    System.out.println(\"n(): \" + s);\n  }\n\n  class InnerPrivateString {\n    private InnerPrivateString(String s) {\n      n(s);\n    }\n  }\n\n  class InnerPrivate {\n    private InnerPrivate(long a, int b) {\n      n(a + \"+\" + b);\n    }\n  }\n\n  static class InnerStaticPrivateString {\n    private InnerStaticPrivateString(String s) {\n      n(s);\n    }\n  }\n\n  static class InnerStaticPrivate {\n    private InnerStaticPrivate(long a, int b) {\n      n(a + \"+\" + b);\n    }\n  }\n\n  class InnerPublicString {\n    public InnerPublicString(String s) {\n      n(s);\n    }\n  }\n\n  class InnerPublic {\n    public InnerPublic(long a, int b) {\n      n(a + \"+\" + b);\n    }\n  }\n\n  static class InnerStaticPublicString {\n    public InnerStaticPublicString(String s) {\n      n(s);\n    }\n  }\n\n  static class InnerStaticPublic {\n    public InnerStaticPublic(long a, int b) {\n      n(a + \"+\" + b);\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestAnonymousParamNames.java",
    "content": "/*\n * Copyright 2000-2016 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.io.File;\n\npublic class TestAnonymousParamNames {\n  private final Clazz reference = new Clazz(0, false) {};\n\n  private class Clazz {\n    public Clazz(long paramL, boolean paramB) {\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestAnonymousParams.java",
    "content": "/*\n * Copyright 2000-2016 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class TestAnonymousParams {\n  void foo(InputStream in, int a) throws IOException {\n    FilterInputStream filterInputStream = new FilterInputStream(in) {\n      @Override\n      public int read() throws IOException {\n        return a;\n      }\n    };\n    filterInputStream.read();\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestAnonymousSignature.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage pkg;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\n\npublic class TestAnonymousSignature {\n  public static void main(String[] args) {\n    // anonymous from class\n    System.out.println(new ArrayList<String>() {\n      @Override\n      public int size() {\n        return super.size();\n      }\n    });\n\n    // anonymous from class\n    System.out.println(new Comparator<String>() {\n      @Override\n      public int compare(String o1, String o2) {\n        return 0;\n      }\n    });\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestAsserts.java",
    "content": "/*\n * Copyright 2000-2016 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\n\npublic class TestAsserts {\n  public static int foo() {\n    int i=1;\n    assert i > 1;\n    assert i> 1 && i < 5;\n    return 1;\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestClashName.java",
    "content": "package pkg;\n\n/*\n * The names SharedName[123] are shared between variables in local|parent class and class names.\n * Where approprate, the classes have to be referenced by fully qualified names\n */\n\nclass SharedName1 {\n  static int f = 0;\n\n  static int getF() {\n    return f;\n  }\n}\n\nclass SharedName2 {\n  static int f = 0;\n}\n\nclass SharedName3 {\n  static int f = 0;\n}\n\nclass NonSharedName {\n  static int f = 0;\n\n  static int getF() {\n    return f;\n  }\n}\n\n@interface SharedName4 {\n}\n\nclass SharedName5<T>{\n}\n\nclass TestClashNameParent {\n\n}\n\ninterface TestClashNameIface {\n  public void f();\n}\n// *** Legend for first sentence in comments below:\n// (+) or (-) indicate whether 'pkg' prefix is or is not required when referencing *SharedName*.\n//  The sign is optionally followed by decompiler class name that generates the line that is being commented\n// in a call of getShortName()\n\n@SharedName4 // (-)AnnotationExprent. While a variable named SharedName4 does exist in the annotated class,\n             // lookup process for annotation names never includes variable names.\npublic class TestClashName extends ext.TestClashNameParent implements /*pkg.*/TestClashNameIface { // (+)(-)TextUtil\n  int TestClashNameParent = 0;\n  int TestClashNameIface = 0;\n  int SharedName1 = 0;\n  int SharedName4 = 0;\n  int SharedName5 = 0;\n\n  int i = pkg.SharedName1.f;     // (+)FieldExprent. SharedName1 class name is shadowed by a variable in this class\n  int j = NonSharedName.f;       // (-)FieldExprent. The NonSharedName is not used for other objects in the current scope\n  int k = SharedName2.f;         // (-)FieldExprent. SharedName2 variable is not the current scope\n  int l = pkg.SharedName3.f;     // (+)FieldExprent. SharedName3 class name is shadowed by a variable in parent class\n  int m = pkg.SharedName1.getF();// (+)InvocationExprent.  SharedName1 class name is shadowed by a variable in this class\n  int n = NonSharedName.getF();  // (-)InvocationExprent. The NonSharedName is not used for other objects in the current scope\n  SharedName1 p = null;          // (-)ExprProcessor. While a variable named SharedName1 in current scope does exist,\n                                 // namespace in type declaration does not include variable names in a scope\n  SharedName5<SharedName1> q = null;//(-)(-)GenericMain (both names).  While a variable named SharedName1 does exist in current scope,\n                                  // lookup for generic parameters never includes variable names\n\n  @SharedName4 // (-)AnnotationExprent. While a variable named SharedName4 does exist in current scope,\n               // lookup process for annotation names never includes variable names.\n  public int m() {\n    int SharedName2 = i;\n    pkg.SharedName1.f = j;       // (+)FieldExprent. SharedName1 class name is shadowed by a variable in this class\n    int x = pkg.SharedName2.f;   // (+)FieldExprent. SharedName2 class name is shadowed by a variable in this method\n    NonSharedName.f = k;         // (-)ExprProcessor. The NonSharedName is not used for other objects in the current scope\n    int y = NonSharedName.f;     // (-)FieldExprent. The NonSharedName is not used for other objects in the current scope\n    return SharedName2 + x + y;\n  }\n\n  @Override\n  public void f() {}\n}\n"
  },
  {
    "path": "testData/src/pkg/TestClassCast.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.util.*;\n\npublic class TestClassCast {\n  public void test(List list) {\n    List a = list;\n    if (a != null) {\n      (a = new ArrayList(a)).add(\"23\");\n    }\n    System.out.println(a.size());\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestClassFields.java",
    "content": "package pkg;\n\npublic class TestClassFields {\n  private static class Inner {\n    private static int staticMutable;\n  }\n\n  private static int[] sizes;\n  private static String[] names;\n\n  private static final int SIZE;\n\n  static {\n    names = new String[]{\"name1\", \"name2\"};\n    sizes = new int[names.length];\n\n    Inner.staticMutable = 3;\n    SIZE = Inner.staticMutable;\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestClassLambda.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.lang.annotation.Annotation;\nimport java.util.*;\nimport java.util.Arrays;\nimport java.util.function.IntBinaryOperator;\nimport java.util.function.Supplier;\n\npublic class TestClassLambda {\n\n  public int field = 0;\n\n  public void testLambda() {\n    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);\n    int b = (int)Math.random();\n\n    list.forEach(n -> {\n      int a = 2 * n;\n      System.out.println(a + b + field);\n    });\n  }\n\n  public void testLambda1() {\n    int a = (int)Math.random();\n    Runnable r1 = () -> { System.out.println(\"hello1\" + a); };\n    Runnable r2 = () -> { System.out.println(\"hello2\" + a); };\n  }\n\n  public void testLambda2() {\n    reduce((left, right) -> Math.max(left, right));\n  }\n\n  public void testLambda3() { // IDEA-127301\n    reduce(Math::max);\n  }\n\n  public void testLambda4() {\n    reduce(TestClassLambda::localMax);\n  }\n\n  public void testLambda5() {\n    String x = \"abcd\";\n    function(x::toString);\n  }\n\n  public void testLambda6() {\n    List<String> list = new ArrayList<String>();\n    int bottom = list.size() * 2;\n    int top = list.size() * 5;\n    list.removeIf(s -> (bottom >= s.length() && s.length() <= top));\n  }\n\n  public static void testLambda7(Annotation[] annotations) {\n    Arrays.stream(annotations).map(Annotation::annotationType);\n  }\n\n  public static OptionalInt reduce(IntBinaryOperator op) {\n    return null;\n  }\n\n  public static String function(Supplier<String> supplier) {\n    return supplier.get();\n  }\n\n  public static int localMax(int first, int second) {\n    return 0;\n  }\n\n  public void nestedLambdas() {\n    int a =5;\n    Runnable r1 = () -> {\n      Runnable r2 = () -> { System.out.println(\"hello2\" + a); };\n      System.out.println(\"hello1\" + a);\n    };\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestClassLoop.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestClassLoop {\n\n  public static void testSimpleInfinite() {\n\n    while (true) {\n      System.out.println();\n    }\n  }\n\n  public static void testFinally() {\n\n    boolean a = (Math.random() > 0);\n\n    while (true) {\n      try {\n        if (!a) {\n          return;\n        }\n      }\n      finally {\n        System.out.println(\"1\");\n      }\n    }\n  }\n\n  public static void testFinallyContinue() {\n\n    boolean a = (Math.random() > 0);\n\n    for (; ; ) {\n      try {\n        System.out.println(\"1\");\n      }\n      finally {\n        if (a) {\n          System.out.println(\"3\");\n          continue;\n        }\n      }\n\n      System.out.println(\"4\");\n    }\n  }\n\n  public static int testWhileCombined(String in) {\n    int len = in.length();\n    int i = 0;\n    boolean decSeen = false;\n    boolean signSeen = false;\n    int decPt = 0;\n    char c;\n    int nLeadZero = 0;\n    int nTrailZero= 0;\n\n    skipLeadingZerosLoop:\n    while (i < len) {\n      c = in.charAt(i);\n      if (c == '0') {\n        nLeadZero++;\n      } else if (c == '.') {\n        if (decSeen) {\n          // already saw one ., this is the 2nd.\n          throw new NumberFormatException(\"multiple points\");\n        }\n        decPt = i;\n        if (signSeen) {\n          decPt -= 1;\n        }\n        decSeen = true;\n      } else {\n        break skipLeadingZerosLoop;\n      }\n      i++;\n    }\n    return decPt;\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestClassNestedInitializer.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestClassNestedInitializer {\n  public String secret;\n\n  public void test() {\n    TestClassNestedInitializer obj = new TestClassNestedInitializer() { {secret = \"one\";} };\n    System.out.println(obj.secret);\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestClassSimpleBytecodeMapping.java",
    "content": "package pkg;\n\nimport java.lang.Override;\nimport java.lang.Runnable;\n\npublic class TestClassSimpleBytecodeMapping {\n\n  public TestClassSimpleBytecodeMapping() {}\n  \n  public int test() {\n    \n    System.out.println(\"before\");\n\n    run(new Runnable() {\n      @Override\n      public void run() {\n        System.out.println(\"Runnable\");\n      }\n    });\n\n    test2(\"1\");\n\n    if(Math.random() > 0) {\n      System.out.println(\"0\");\n      return 0;\n    } else {\n      System.out.println(\"1\");\n      return 1;\n    }\n  }\n\n  public void test2(String a) {\n    try {\n      Integer.parseInt(a);\n    } catch (Exception e) {\n      System.out.println(e);\n    } finally {\n      System.out.println(\"Finally\");\n    }\n  }\n\n  public class InnerClass {\n    public void print() {\n      System.out.println(\"Inner\");\n    }\n  }\n\n  void run(Runnable r) {\n    r.run();\n  }\n\n  public class InnerClass2 {\n    public void print() {\n      System.out.println(\"Inner2\");\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestClassSwitch.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestClassSwitch {\n\n  public void testCaseOrder(int a) {\n\n    switch (a) {\n      case 13:\n        System.out.println(13);\n        return;\n      case 5:\n        System.out.println(5);\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestClassTypes.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TestClassTypes {\n\n  public void testBoolean() {\n\n    byte var7 = 0;\n    long time = System.currentTimeMillis();\n\n    if (time % 2 > 0) {\n      var7 = 1;\n    }\n    else if (time % 3 > 0) {\n      var7 = 2;\n    }\n\n    if (var7 == 1) {\n      System.out.println();\n    }\n  }\n\n  public boolean testBit(int var0) {\n    return (var0 & 1) == 1;\n  }\n\n  public void testSwitchConsts(int a) {\n\n    switch (a) {\n      case 88:\n        System.out.println(\"1\");\n        break;\n      case 656:\n        System.out.println(\"2\");\n        break;\n      case 65201:\n      case 65489:\n        System.out.println(\"3\");\n    }\n  }\n\n  public void testAssignmentType(List list) {\n\n    List a = list;\n\n    if (a != null) {\n      (a = new ArrayList(a)).add(\"23\");\n    }\n\n    System.out.println(a.size());\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestClassVar.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\n\npublic class TestClassVar {\n\n  private boolean field_boolean = (Math.random() > 0);\n  public int field_int = 0;\n\n  public void testFieldSSAU() {\n\n    for (int i = 0; i < 10; i++) {\n\n      try {\n        System.out.println();\n      }\n      finally {\n        if (field_boolean) {\n          System.out.println();\n        }\n      }\n    }\n  }\n\n  public Long testFieldSSAU1() { // IDEA-127466\n    return new Long(field_int++);\n  }\n\n  public void testComplexPropagation() {\n\n    int a = 0;\n\n    while (a < 10) {\n\n      int b = a;\n\n      for (; a < 10 && a == 0; a++) {\n      }\n\n      if (b != a) {\n        System.out.println();\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestCodeConstructs.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nclass TestCodeConstructs {\n  void expressions() {\n    new String().hashCode();\n  }\n\n  private int count = 0;\n  Integer fieldIncrement() {\n    return new Integer(count++);\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestComplexInstanceOfRecordPatternJavac.java",
    "content": "package pkg;\n\npublic class TestComplexInstanceOfRecordPatternJavac {\n    public static void main(String[] args) {\n\n    }\n\n    public static void instanceOfTest1(Object o) {\n        if (o instanceof R(R(Object s1))) {\n            System.out.println(s1);\n            System.out.println(s1);\n        }\n        System.out.println(\"1\");\n    }\n\n    public static void instanceOfTest2(Object o) {\n        if (o instanceof R(R(String s1))) {\n            System.out.println(s1);\n            System.out.println(s1);\n        }\n        System.out.println(\"4\");\n    }\n\n    public static void instanceOfTest3(Object o) {\n        if (o instanceof R2(String s1, Object s2)) {\n            System.out.println(s1);\n            System.out.println(s1);\n        }\n        System.out.println(\"12\");\n    }\n\n    public static void instanceOfTest3_2(Object o) {\n        if (o instanceof R2(Object s2, String s1)) {\n            System.out.println(s1);\n            System.out.println(s1);\n        }\n        System.out.println(\"3\");\n    }\n\n    public static void instanceOfTest4(Object o) {\n        if (o instanceof R2(String s1, R(String s))) {\n            System.out.println(s1);\n            System.out.println(s);\n\n            if (o instanceof R2(String s2, R(String s3))) {\n                System.out.println(s2);\n                System.out.println(s3);\n            }\n        }\n        System.out.println(\"1\");\n    }\n\n    record R(Object o) {\n    }\n\n    record R2(Object o1, Object o2) {\n    }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestConstType.java",
    "content": "package pkg;\n\npublic class TestConstType {\n    private char lineBreak = '\\n';\n    private char zero = 0;\n\n    public void setLineBreak(char os) {\n        switch (os) {\n            case 'u':\n                lineBreak = '\\r';\n                break;\n\n            case 'w':\n                lineBreak = '\\n';\n                break;\n        }\n    }\n\n    public void init() {\n        setLineBreak('w');\n    }\n\n    public String convertIndentation(String text) {\n        if (text.charAt(0) == '\\t') {\n            text = text.replace('\\t', ' ');\n        }\n        return text;\n    }\n\n    public void printalot() {\n        System.out.println('a');\n        System.out.println('\\t');\n\n        System.out.println(0);\n        System.out.println(65);\n        System.out.println(120);\n        System.out.println(32760);\n        System.out.println(32761);\n        System.out.println(35000);\n        System.out.println(50000);\n        System.out.println(128000);\n        System.out.println(60793);\n        System.out.println(60737);\n        System.out.println(60777);\n        System.out.println(60785);\n        System.out.println(60835);\n        System.out.println(60843);\n        System.out.println(60851);\n        System.out.println(60859);\n        System.out.println(1048576);\n        System.out.println(49152);\n        System.out.println(44100);\n        System.out.println(44101);\n        System.out.println(44102);\n        System.out.println(44103);\n        System.out.println(60000);\n        System.out.println(64000);\n        System.out.println(65000);\n        System.out.println(45000);\n    }\n\n    public char guessType(int val) {\n        if (0 <= val && val <= 127) {\n            return 'X';\n        }\n        else if (-128 <= val && val <= 127) {\n            return 'B';\n        }\n        else if (128 <= val && val <= 32767) {\n            return 'Y';\n        }\n        else if (-32768 <= val && val <= 32767) {\n            return 'S';\n        }\n        else if (32768 <= val && val <= 0xFFFF) {\n            return 'C';\n        }\n        else {\n            return 'I';\n        }\n    }\n\n    public int getTypeMaxValue(char type) {\n        int maxValue;\n        switch (type) {\n            case 'X':\n                maxValue = 128;\n                break;\n            case 'B':\n                maxValue = 127;\n                break;\n            case 'Y':\n                maxValue = 32768;\n                break;\n            case 'S':\n                maxValue = 32767;\n                break;\n            case 'C':\n                maxValue = 0xFFFF;\n                break;\n            default:\n                maxValue = Integer.MAX_VALUE;\n        }\n        return maxValue;\n    }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestConstants.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestConstants {\n  static final boolean T = true;\n  static final boolean F = false;\n\n  static final char C0 = '\\n';\n  static final char C1 = 'a';\n  static final char C2 = 512;\n\n  static final byte BMin = Byte.MIN_VALUE;\n  static final byte BMax = Byte.MAX_VALUE;\n\n  static final short SMin = Short.MIN_VALUE;\n  static final short SMax = Short.MAX_VALUE;\n\n  static final int IMin = Integer.MIN_VALUE;\n  static final int IMax = Integer.MAX_VALUE;\n\n  static final long LMin = Long.MIN_VALUE;\n  static final long LMax = Long.MAX_VALUE;\n\n  static final float FNan = Float.NaN;\n  static final float FNeg = Float.NEGATIVE_INFINITY;\n  static final float FPos = Float.POSITIVE_INFINITY;\n  static final float FMin = Float.MIN_VALUE;\n  static final float FMax = Float.MAX_VALUE;\n  static final float FMinNormal = Float.MIN_NORMAL;\n\n  static final double DNan = Double.NaN;\n  static final double DNeg = Double.NEGATIVE_INFINITY;\n  static final double DPos = Double.POSITIVE_INFINITY;\n  static final double DMin = Double.MIN_VALUE;\n  static final double DMax = Double.MAX_VALUE;\n  static final double FDoubleNormal = Double.MIN_NORMAL;\n\n  static final double PI = Math.PI;\n  static final double E = Math.E;\n}"
  },
  {
    "path": "testData/src/pkg/TestConstantsAsIs.java",
    "content": "package pkg;\n\npublic class TestConstantsAsIs {\n    static final boolean T = true;\n    static final boolean F = false;\n\n    static final char C0 = '\\n';\n    static final char C1 = 'a';\n    static final char C2 = 512;\n\n    static final byte BMin = Byte.MIN_VALUE;\n    static final byte BMax = Byte.MAX_VALUE;\n\n    static final short SMin = Short.MIN_VALUE;\n    static final short SMax = Short.MAX_VALUE;\n\n    static final int IMin = Integer.MIN_VALUE;\n    static final int IMax = Integer.MAX_VALUE;\n\n    static final long LMin = Long.MIN_VALUE;\n    static final long LMax = Long.MAX_VALUE;\n\n    static final float FNan = Float.NaN;\n    static final float FNeg = Float.NEGATIVE_INFINITY;\n    static final float FPos = Float.POSITIVE_INFINITY;\n    static final float FMin = Float.MIN_VALUE;\n    static final float FMax = Float.MAX_VALUE;\n\n    static final double DNan = Double.NaN;\n    static final double DNeg = Double.NEGATIVE_INFINITY;\n    static final double DPos = Double.POSITIVE_INFINITY;\n    static final double DMin = Double.MIN_VALUE;\n    static final double DMax = Double.MAX_VALUE;\n\n    static @interface A {\n        Class<?> value();\n    }\n\n    @A(byte.class) void m1() { }\n    @A(char.class) void m2() { }\n    @A(double.class) void m3() { }\n    @A(float.class) void m4() { }\n    @A(int.class) void m5() { }\n    @A(long.class) void m6() { }\n    @A(short.class) void m7() { }\n    @A(boolean.class) void m8() { }\n    @A(void.class) void m9() { }\n    @A(java.util.Date.class) void m10() { }\n}"
  },
  {
    "path": "testData/src/pkg/TestConstructorReference.java",
    "content": "/*\n * Copyright 2000-2016 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npublic class TestConstructorReference {\n  public TestConstructorReference() {\n  }\n\n  void boo() {\n    Runnable aNew = TestConstructorReference::new;\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestDebugSymbols.java",
    "content": "package pkg;\n\n// compile with `javac -g ...`\nclass TestDebugSymbols {\n  private int m() {\n    String text = \"text\";\n    long prolonged = 42L;\n    float decimated = prolonged / 10.0f;\n    double doubled = 2 * decimated;\n    return (text + \":\" + prolonged + \":\" + decimated + \":\" + doubled).length();\n  }\n\n  public void test() {\n    int i = 0;\n    int count = 0;\n    do {\n      i += count++;\n    } while( i < 10 );\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestDeprecations.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic abstract class TestDeprecations {\n  /** @deprecated */\n  public int byComment;\n\n  @Deprecated\n  public int byAnno;\n\n  /** @deprecated */\n  public void byComment() {\n    int a =5;\n  }\n\n  /** @deprecated */\n  public abstract void byCommentAbstract();\n\n  @Deprecated\n  public void byAnno() {\n    int a =5;\n  }\n\n  @Deprecated\n  public abstract void byAnnoAbstract();\n\n  /** @deprecated */\n  public static class ByComment {\n    int a =5;\n\n    void foo() {\n      int x = 5;\n    }\n  }\n\n  @Deprecated\n  public static class ByAnno {\n    int a =5;\n\n    void foo() {\n      int x = 5;\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestEmptyBlocks.java",
    "content": "/*\n * Copyright 2000-2016 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport java.util.Random;\n\npublic class TestEmptyBlocks {\n\n  public static void foo() {\n    try {\n      int a = 0;  //make sure whole try/catch not removed by compiler\n    } catch(Exception e) {\n\n    }\n\n    for(int i = 0; i < 5; i++) {\n\n    }\n\n    while(new Random().nextBoolean()) {\n\n    }\n\n    if(new Random().nextBoolean()) {\n\n    }\n  }\n\n\n\n\n\n\n\n\n\n}"
  },
  {
    "path": "testData/src/pkg/TestEnum.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic enum TestEnum {\n  E1,\n  E2() {\n    @Override\n    public void m() { }\n  },\n  E3(\"-\", Type.ODD),\n  E4(\"+\", Type.EVEN) {\n    @Override\n    public void m() { }\n  };\n\n  private enum Type {ODD, EVEN}\n\n  public void m() { }\n\n  private String s;\n\n  private TestEnum() { this(\"?\", null); }\n  private TestEnum(@Deprecated String s, Type t) { this.s = s; }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestExtendingSubclass.java",
    "content": "package pkg;\n\nimport java.math.BigDecimal;\n\npublic class TestExtendingSubclass {\n\n  class Subclass1 {\n    Subclass1(String name) {\n    }\n  }\n\n  class Subclass2 extends Subclass1 {\n    Subclass2(String name) {\n      super(name);\n    }\n  }\n\n}\n"
  },
  {
    "path": "testData/src/pkg/TestExtendsList.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestExtendsList {\n  static <T extends Comparable<? super T>> T m1(T t) {\n    return null;\n  }\n\n  static <T extends Object & Comparable<? super T>> T m2(T t) {\n    return null;\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestFieldSingleAccess.jasm",
    "content": "/**\n * This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n * using <code>asmtools jasm -g *.jasm</code> command line.\n */\npackage  pkg;\n\nsuper public final class TestFieldSingleAccess\n\tversion 52:0\n{\n\npublic Field field:\"Ljava/lang/Integer;\";\n\npublic Method \"<init>\":\"()V\"\n\tstack 1 locals 1\n{\n\t\taload_0;\n\t\tinvokespecial\tMethod java/lang/Object.\"<init>\":\"()V\";\n\t\treturn;\n}\n\npublic final Method test:\"()V\"\n\tstack 2 locals 1\n{\n\t\taload_0;\n\t\tgetfield\tField field:\"Ljava/lang/Integer;\";\n\t\tdup;\n\t\tifnull\tL17;\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tswap;\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(Ljava/lang/Object;)V\";\n\tL17:\tstack_frame_type same;\n\t\treturn;\n}\n\npublic final Method test1:\"()V\"\n\tstack 2 locals 3\n{\n\t\taload_0;\n\t\tgetfield\tField field:\"Ljava/lang/Integer;\";\n\t\tdup;\n\t\tastore_1;\n\t\tmonitorenter;\n\t\ttry t0;\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tbipush\t49;\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(C)V\";\n\t\taload_1;\n\t\tmonitorexit;\n\t\tendtry t0;\n\t\tgoto\tL25;\n\t\tcatch t0 #0;\n\t\tcatch t1 #0;\n\t\ttry t1;\n\t\tstack_frame_type full;\n\t\tlocals_map class TestFieldSingleAccess, class java/lang/Object;\n\t\tstack_map class java/lang/Throwable;\n\t\tastore_2;\n\t\taload_1;\n\t\tmonitorexit;\n\t\tendtry t1;\n\t\taload_2;\n\t\tathrow;\n\tL25:\tstack_frame_type chop1;\n\t\treturn;\n}\n\n} // end Class TestFieldSingleAccess"
  },
  {
    "path": "testData/src/pkg/TestGenericArgs.java",
    "content": "package pkg;\n\npublic class TestGenericArgs {\n  private static <T> void genericSingle(Class<T> param) { }\n\n  private static <T> void genericVarArgs(Class<T>... param) { }\n\n  private static <T> void genericArray(Class<T>[] param) { }\n\n  private static <T> void single(Class param) { }\n\n  private static <T> void varArgs(Class... param) { }\n\n  private static <T> void array(Class[] param) { }\n}"
  },
  {
    "path": "testData/src/pkg/TestGroovyClass.groovy",
    "content": "package pkg\n\nimport java.util.concurrent.Callable\n\nclass TestGroovyClass {\n  static class Nested { }\n  class Inner { }\n\n  final Nested n = new Nested()\n  final Inner i = new Inner()\n  final Runnable r = { println(\"I'm runnable\") }\n  final Callable<String> c = { \"I'm callable\" }\n}"
  },
  {
    "path": "testData/src/pkg/TestGroovyTrait.groovy",
    "content": "package pkg\n\ntrait TestGroovyTrait {\n  def myField = 42\n  def myMethod() {\n    42\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestIffSimplification.java",
    "content": "package pkg;\n\nimport java.lang.Math;\n\npublic class TestIffSimplification {\n    public int simpleIff(boolean status, int[] values) {\n        return status ? values[0] : values[1];\n    }\n\n    public int simpleIf(boolean status, int[] values) {\n        if (status) {\n            return values[0];\n        }\n        else {\n            return values[1];\n        }\n    }\n\n    public int nestedIf(boolean status, boolean condition, int[] values) {\n        if (status) {\n            if (condition) {\n                return values[2];\n            }\n            else {\n                return values[0];\n            }\n        }\n        else {\n            return values[1];\n        }\n    }\n\n    public int compareTo(int mc1, int mc2, byte csg1, byte csg2, double score1, double score2, int doc1, int doc2) {\n        if (mc1 != mc2) {\n            return mc1 < mc2 ? 1 : -1;\n        }\n\n        if (csg1 != csg2) {\n            return csg1 < csg2 ? 1 : -1;\n        }\n\n        if (Math.abs(score1 - score2) < 1e-6) {\n            return doc1 < doc2 ? -1 : 1;\n        }\n\n        return score1 < score2 ? 1 : -1;\n    }\n}"
  },
  {
    "path": "testData/src/pkg/TestIllegalVarName.kt",
    "content": "package pkg\n\nclass TestIllegalVarName {\n  fun m(`this`: String, `enum`: Int): String {\n    return `this` + '/' + `enum`\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestInUse.java",
    "content": "package pkg;\n\npublic class TestInUse {\n  public int getInt() {\n    return 42;\n  }\n\n  protected int reuse() {\n    int i = 0, d = 0;\n    int result = 0;\n    do {\n      d = getInt();\n      result -= d;\n    }\n    while (++i < 10);\n    return result;\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestInheritanceChainCycle.jasm",
    "content": "/**\n * This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n * using <code>asmtools jasm -g *.jasm</code> command line.\n */\npackage  pkg;\n\nsuper public class TestInheritanceChainCycle\n\textends TestInheritanceChainCycle\n\tversion 52:0\n{\n  public Method \"<init>\":\"()V\"\n\tstack 1 locals 1\n  {\n\t\taload_0;\n\t\tinvokespecial\tMethod java/lang/Object.\"<init>\":\"()V\";\n\t\treturn;\n  }\n  public Method printMessage:\"()V\"\n\tstack 2 locals 1\n  {\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tldc\tString \"Hello, bug!\";\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(Ljava/lang/String;)V\";\n\t\treturn;\n  }\n\n} // end Class TestInheritanceChainCycle"
  },
  {
    "path": "testData/src/pkg/TestInner2.java",
    "content": "package pkg;\n\nclass TestInner2 {\n  private TestInner2() {}\n  private TestInner2(int a) {}\n\n  class Another extends TestInner2 {\n    Another() {\n    }\n  }\n\n  static class AnotherStatic extends TestInner2 {\n    AnotherStatic() {\n    }\n  }\n\n  class Another2 extends TestInner2 {\n    Another2() {\n      super(2);\n    }\n  }\n\n  static class AnotherStatic2 extends TestInner2 {\n    AnotherStatic2() {\n      super(2);\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestInnerClassConstructor.java",
    "content": "package pkg;\n\nclass TestInnerClassConstructor {\n  void l() {\n    new Inner(\"text\");\n  }\n\n  void m() {\n    new Another(3, 4);\n  }\n\n  void n(String s) {\n    System.out.println(\"n(): \" + s);\n  }\n\n  final class Inner {\n    private Inner(String s) {\n      n(s);\n    }\n  }\n\n  private class Another {\n    private Another(int a, int b) {\n      n(a + \"+\" + b);\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestInnerLocal.java",
    "content": "package pkg;\n\npublic class TestInnerLocal {\n  public static void testStaticMethod() {\n    class Inner {\n      final String x;\n      public Inner(@Deprecated String x) {\n        this.x = x;\n      }\n    }\n    new Inner(\"test\");\n    new Inner1Static(\"test\");\n    new Inner1Static.Inner2Static(\"test\");\n  }\n\n  public void testMethod() {\n    class Inner {\n      final String x;\n      public Inner(@Deprecated String x) {\n        this.x = x;\n      }\n    }\n    new Inner(\"test\");\n    new Inner1Static(\"test\");\n    new Inner1(\"test\");\n    new Inner1Static.Inner2Static(\"test\");\n  }\n\n  class Inner1 {\n    final String x;\n    public Inner1(@Deprecated String x) {\n      this.x = x;\n    }\n  }\n\n  static class Inner1Static {\n    final String x;\n    public Inner1Static(@Deprecated String x) {\n      this.x = x;\n    }\n\n    public static class Inner2Static {\n      final String x;\n      public Inner2Static(@Deprecated String x) {\n        this.x = x;\n      }\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestInnerSignature.java",
    "content": "package pkg;\n\npublic class TestInnerSignature<A,B,C> {\n  A a;\n  B b;\n  C c;\n\n  public TestInnerSignature(A a, @Deprecated B b,C c) {\n    this.a = a;\n    this.b = b;\n    this.c = c;\n  }\n\n  public class Inner {\n    A a;\n    B b;\n    C c;\n\n    public Inner(A a, @Deprecated B b, C c) {\n      this.a = a;\n      this.b = b;\n      this.c = c;\n    }\n  }\n\n  public static class InnerStatic<A,B,C> {\n    A a;\n    B b;\n    C c;\n\n    public InnerStatic(A a, @Deprecated B b, C c) {\n      this.a = a;\n      this.b = b;\n      this.c = c;\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestIntVarMerge.java",
    "content": "package pkg;\n\npublic class TestIntVarMerge {\n  public int test1() {\n    int hash = 7;\n    hash = 23 * hash;\n    hash *= 23;\n    return hash;\n  }\n\n  public void test2() {\n    int k = 3;\n    System.out.println(k);\n    k++;\n    System.out.println(k);\n    ++k;\n    System.out.println(k);\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestInterfaceFields.java",
    "content": "package pkg;\n\nimport java.math.BigDecimal;\n\npublic interface TestInterfaceFields {\n  BigDecimal BIG_ZERO = BigDecimal.ZERO;\n  int MAX_BYTE_VALUE = Byte.MAX_VALUE;\n}"
  },
  {
    "path": "testData/src/pkg/TestInterfaceMethods.java",
    "content": "package pkg;\n\npublic interface TestInterfaceMethods {\n  static void staticMethod() {}\n  default void defaultMethod() {}\n}"
  },
  {
    "path": "testData/src/pkg/TestInterfaceSuper.java",
    "content": "package pkg;\n\npublic interface TestInterfaceSuper {\n  default void defaultMethod() {}\n\n  class Impl implements TestInterfaceSuper {\n    public void defaultMethod() {\n      TestInterfaceSuper.super.defaultMethod();\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestInvertedFloatComparison.java",
    "content": "/*\n * Copyright 2000-2018 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestInvertedFloatComparison {\n\n  public boolean less(double a, double b) {\n    return a < b;\n  }\n\n  public boolean less(int a, int b) {\n    return a < b;\n  }\n\n  public boolean notLess(double a, double b) {\n    return !(a < b);\n  }\n\n  public boolean notLess(int a, int b) {\n    return !(a < b);\n  }\n\n  public boolean greater(double a, double b) {\n    return a > b;\n  }\n\n  public boolean greater(int a, int b) {\n    return a > b;\n  }\n\n  public boolean notGreater(double a, double b) {\n    return !(a > b);\n  }\n\n  public boolean notGreater(int a, int b) {\n    return !(a > b);\n  }\n\n  public boolean lessEqual(double a, double b) {\n    return a <= b;\n  }\n\n  public boolean lessEqual(int a, int b) {\n    return a <= b;\n  }\n\n  public boolean notLessEqual(double a, double b) {\n    return !(a <= b);\n  }\n\n  public boolean notLessEqual(int a, int b) {\n    return !(a <= b);\n  }\n\n  public boolean greaterEqual(double a, double b) {\n    return a >= b;\n  }\n\n  public boolean greaterEqual(int a, int b) {\n    return a >= b;\n  }\n\n  public boolean notGreaterEqual(double a, double b) {\n    return !(a >= b);\n  }\n\n  public boolean notGreaterEqual(int a, int b) {\n    return !(a >= b);\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestKotlinConstructor.kt",
    "content": "private fun foo(list: Collection<String>): List<Mapping> {\n    return list.map {\n        Mapping(it as String)\n    }.toList()\n}\n\nclass Mapping(c: String) {\n}\n"
  },
  {
    "path": "testData/src/pkg/TestLambdaParams.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.util.function.BinaryOperator;\nimport java.util.function.Function;\n\npublic class TestLambdaParams {\n  public static void toCollection(Object collectionFactory) {\n    Class a = null;\n    Function f = r1 -> collectionFactory;\n    Function f1 = r1 -> a;\n    Function f2 = r1 -> r1;\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestLocalClass.java",
    "content": "package pkg;\n\nimport java.lang.Override;\nimport java.lang.Runnable;\n\npublic abstract class TestLocalClass {\n  void foo() {\n    int a =5;\n    class Local{\n      void foo() {\n        int b = 5;\n        int v = 5;\n      }\n    };\n    Local l = new Local();\n    l.foo();\n  }\n\n  void boo() {\n    int a =5;\n  }\n\n  void zoo() {\n    int a =5;\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestLocalsNames.java",
    "content": "/*\n * Copyright 2000-2016 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.io.File;\n\npublic class TestLocalsNames {\n  private static void rename(File file, boolean recursively) {\n    if (file.isDirectory()) {\n      long start = System.currentTimeMillis();\n\n      File[] files = file.listFiles();\n      for (File s : files) {\n        File dest = new File(s.getAbsolutePath() + \".tmp\");\n        assert s.renameTo(dest) : \"unable to rename \" + s + \" to \" + dest;\n      }\n\n      long elapsed = System.currentTimeMillis() - start;\n      System.out.println(\"took \" + elapsed + \"ms (\" + elapsed / files.length + \"ms per dir)\");\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestLocalsSignature.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage pkg;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class TestLocalsSignature {\n  public static void main(String[] args) {\n    List<String> s = new ArrayList<String>();\n    s.add(\"xxx\");\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestMemberAnnotations.java",
    "content": "package pkg;\n\nimport java.lang.annotation.*;\n\nclass TestMemberAnnotations {\n  @Retention(RetentionPolicy.RUNTIME)\n  @interface A { String value() default \"\"; }\n\n  @A(\"const\") public static final int CONST = 42;\n  @A(\"field\") private int f;\n\n  @A(\"return\") private int f(@A(\"arg\") int i) { return i + f + CONST; }\n}"
  },
  {
    "path": "testData/src/pkg/TestMethodParameters.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestMethodParameters {\n  TestMethodParameters(@Deprecated int p01) { }\n  void m1(@Deprecated int p02) { }\n  static void m2(@Deprecated int p03) { }\n\n  class C1 {\n    C1(@Deprecated int p11) { }\n    void m(@Deprecated int p12) { }\n  }\n\n  static class C2 {\n    C2(@Deprecated int p21) { }\n    void m1(@Deprecated int p22) { }\n    static void m2(@Deprecated int p23) { }\n  }\n\n  void local() {\n    class Local {\n      Local(@Deprecated int p31) { }\n      void m(@Deprecated int p32) { }\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestMethodParametersAttr.java",
    "content": "package pkg;\n\n// compile with `javac -parameters ...`\npublic class TestMethodParametersAttr {\n  TestMethodParametersAttr(int p01) { System.out.print(p01); }\n  void m1(int p02) { System.out.print(p02); }\n  static void m2(int p03) { System.out.print(p03); }\n\n  class C1 {\n    C1(int p11) { System.out.print(p11); }\n    void m(int p12) { System.out.print(p12); }\n  }\n\n  static class C2 {\n    C2(int p21) { System.out.print(p21); }\n    void m1(int p22) { System.out.print(p22); }\n    static void m2(int p23) { System.out.print(p23); }\n  }\n\n  void local() {\n    class Local {\n      Local(int p31) { System.out.print(p31); }\n      void m(int p32) { System.out.print(p32); }\n    }\n  }\n\n  interface I1 {\n      void m1(int p41);\n      void m2(final int p42);\n  }\n\n  abstract class C3 {\n    abstract void m1(int p51);\n    abstract void m2(final int p52);\n  }\n\n  static abstract class C4 {\n    abstract void m1(int p61);\n    abstract void m2(final int p62);\n  }\n\n  enum E1 {\n    ;\n    E1(int p71) { System.out.print(p71); }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestMethodReferenceLetterClass.java",
    "content": "/*\n * Copyright 2000-2016 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport java.util.function.Consumer;\n\npublic class TestMethodReferenceLetterClass {\n  void boo() {\n    Consumer<R> foo = R::foo;\n  }\n}\n\nclass R {\n  void foo() {}\n}"
  },
  {
    "path": "testData/src/pkg/TestMethodReferenceSameName.java",
    "content": "/*\n * Copyright 2000-2016 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npublic class TestMethodReferenceSameName {\n  R1 r;\n\n  private void foo() {\n    ((Runnable)r::foo).run();\n  }\n\n  class R1 {\n    void foo() {}\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestMissingConstructorCallBad.jasm",
    "content": "/**\n * This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n * using <code>asmtools jasm -g *.jasm</code> command line.\n */\npackage  pkg;\n\nsuper public class TestMissingConstructorCallBad\n\tversion 52:0\n{\n\nprivate Method \"<init>\":\"()V\"\n\tstack 2 locals 1\n{\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tldc\tString \"Nobody will see what we do here!\";\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(Ljava/lang/String;)V\";\n\n        aload_0;\n\t\taconst_null;\n\t\tinvokespecial\tMethod \"<init>\":\"(Ljava/lang/Object;)V\";\n\t\treturn;\n}\n\npublic static varargs Method main:\"([Ljava/lang/String;)V\"\n\tstack 2 locals 2\n{\n\t\ttry t0;\n\t\tnew\tclass TestMissingConstructorCallBad;\n\t\tdup;\n\t\tinvokespecial\tMethod \"<init>\":\"()V\";\n\t\tpop;\n\t\tendtry t0;\n\t\tgoto\tL12;\n\t\tcatch t0 java/lang/Throwable;\n\t\tstack_frame_type stack1;\n\t\tstack_map class java/lang/Throwable;\n\t\tastore_1;\n\tL12:\tstack_frame_type same;\n\t\treturn;\n}\n\n} // end Class TestMissingConstructorCallBad"
  },
  {
    "path": "testData/src/pkg/TestMissingConstructorCallGood.jasm",
    "content": "/**\n * This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n * using <code>asmtools jasm -g *.jasm</code> command line.\n */\npackage  pkg;\n\nsuper public class TestMissingConstructorCallGood\n\tversion 52:0\n{\n\nprivate Method \"<init>\":\"(Ljava/lang/Object;)V\"\n\tstack 1 locals 2\n{\n\t\taload_0;\n\t\tinvokespecial\tMethod java/lang/Object.\"<init>\":\"()V\";\n\t\treturn;\n}\n\nprivate Method \"<init>\":\"()V\"\n\tstack 2 locals 1\n{\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tldc\tString \"Nobody will see what we do here!\";\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(Ljava/lang/String;)V\";\n\n        aload_0;\n\t\taconst_null;\n\t\tinvokespecial\tMethod \"<init>\":\"(Ljava/lang/Object;)V\";\n\t\treturn;\n}\n\npublic static varargs Method main:\"([Ljava/lang/String;)V\"\n\tstack 2 locals 2\n{\n\t\ttry t0;\n\t\tnew\tclass TestMissingConstructorCallGood;\n\t\tdup;\n\t\tinvokespecial\tMethod \"<init>\":\"()V\";\n\t\tpop;\n\t\tendtry t0;\n\t\tgoto\tL12;\n\t\tcatch t0 java/lang/Throwable;\n\t\tstack_frame_type stack1;\n\t\tstack_map class java/lang/Throwable;\n\t\tastore_1;\n\tL12:\tstack_frame_type same;\n\t\treturn;\n}\n\n} // end Class TestMissingConstructorCallGood"
  },
  {
    "path": "testData/src/pkg/TestNamedSuspendFun2.kt",
    "content": "\nsuspend fun foo2(): Int {\n    while (true) {\n        try {\n            val x = bar()\n            if (x == 0) break\n        } finally {\n            bar()\n        }\n    }\n    return 1\n}\n\nsuspend fun bar(): Int = 0\n"
  },
  {
    "path": "testData/src/pkg/TestParameterizedTypes.java",
    "content": "/*\n * Copyright 2000-2015 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic abstract class TestParameterizedTypes<P> {\n\n  public abstract class Inner<I> { }\n\n  abstract Inner<String> getUnspecificInner();\n\n  abstract TestParameterizedTypes<Number>.Inner<String> getSpecificInner();\n\n}"
  },
  {
    "path": "testData/src/pkg/TestPop2OneDoublePop2.jasm",
    "content": "/**\n * This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n * using <code>asmtools jasm -g *.jasm</code> command line.\n */\npackage  pkg;\n\nsuper public class TestPop2OneDoublePop2\n\tversion 52:0\n{\n\npublic Method \"<init>\":\"()V\"\n\tstack 1 locals 1\n{\n\t\taload_0;\n\t\tinvokespecial\tMethod java/lang/Object.\"<init>\":\"()V\";\n\t\treturn;\n}\n\npublic static varargs Method main:\"([Ljava/lang/String;)V\"\n\tstack 4 locals 1\n{\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tldc int 1234567890;\n\t\tldc2_w double 3.14159265358D;\n\t\tpop2;\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(I)V\";\n\t\treturn;\n}\n\n} // end Class TestPop2OneDoublePop2"
  },
  {
    "path": "testData/src/pkg/TestPop2OneLongPop2.jasm",
    "content": "/**\n * This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n * using <code>asmtools jasm -g *.jasm</code> command line.\n */\npackage  pkg;\n\nsuper public class TestPop2OneLongPop2\n\tversion 52:0\n{\n\npublic Method \"<init>\":\"()V\"\n\tstack 1 locals 1\n{\n\t\taload_0;\n\t\tinvokespecial\tMethod java/lang/Object.\"<init>\":\"()V\";\n\t\treturn;\n}\n\npublic static varargs Method main:\"([Ljava/lang/String;)V\"\n\tstack 4 locals 1\n{\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tldc int 1234567890;\n\t\tldc2_w long 0xCAFEBABE;\n\t\tpop2;\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(I)V\";\n\t\treturn;\n}\n\n} // end Class TestPop2OneLongPop2"
  },
  {
    "path": "testData/src/pkg/TestPop2TwoIntPop2.jasm",
    "content": "/**\n * This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n * using <code>asmtools jasm -g *.jasm</code> command line.\n */\npackage  pkg;\n\nsuper public class TestPop2TwoIntPop2\n\tversion 52:0\n{\n\npublic Method \"<init>\":\"()V\"\n\tstack 1 locals 1\n{\n\t\taload_0;\n\t\tinvokespecial\tMethod java/lang/Object.\"<init>\":\"()V\";\n\t\treturn;\n}\n\npublic static varargs Method main:\"([Ljava/lang/String;)V\"\n\tstack 4 locals 1\n{\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tldc int 1234567890;\n\t\tldc int 0xCAFE;\n\t\tldc int 0xBABE;\n\t\tpop2;\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(I)V\";\n\t\treturn;\n}\n\n} // end Class TestPop2TwoIntPop2"
  },
  {
    "path": "testData/src/pkg/TestPop2TwoIntTwoPop.jasm",
    "content": "/**\n * This code can be assembled with <a href=\"https://wiki.openjdk.org/display/CodeTools/asmtools\">asmtools</a>\n * using <code>asmtools jasm -g *.jasm</code> command line.\n */\npackage  pkg;\n\nsuper public class TestPop2TwoIntTwoPop\n\tversion 52:0\n{\n\npublic Method \"<init>\":\"()V\"\n\tstack 1 locals 1\n{\n\t\taload_0;\n\t\tinvokespecial\tMethod java/lang/Object.\"<init>\":\"()V\";\n\t\treturn;\n}\n\npublic static varargs Method main:\"([Ljava/lang/String;)V\"\n\tstack 4 locals 1\n{\n\t\tgetstatic\tField java/lang/System.out:\"Ljava/io/PrintStream;\";\n\t\tldc int 1234567890;\n\t\tldc int 0xCAFE;\n\t\tldc int 0xBABE;\n\t\tpop;\n\t\tpop;\n\t\tinvokevirtual\tMethod java/io/PrintStream.println:\"(I)V\";\n\t\treturn;\n}\n\n} // end Class TestPop2TwoIntTwoPop"
  },
  {
    "path": "testData/src/pkg/TestPrimitiveNarrowing.java",
    "content": "package pkg;\n\nclass TestPrimitiveNarrowing {\n\n  TestPrimitiveNarrowing(Short value) {\n  }\n\n  static void invocations() {\n    withInteger(null);\n    withShort(null);\n    withByte(null);\n    new TestPrimitiveNarrowing(null);\n  }\n\n  static void withByte(Byte defaultValue) {\n  }\n\n  static void withInteger(Integer defaultValue) {\n  }\n\n  static void withShort(Short defaultValue) {\n  }\n\n}\n"
  },
  {
    "path": "testData/src/pkg/TestPrimitives.java",
    "content": "package pkg;\n\nimport java.util.*;\n\npublic class TestPrimitives {\n\n  public void printAll() {\n    printBoolean(true);\n    printByte((byte) 123);\n    printShort((short) 257);\n    printInt(123);\n    printLong(123L);\n    printFloat(1.23F);\n    printDouble(1.23);\n    printChar('Z');\n\n    printBooleanBoxed(true);\n    printByteBoxed((byte) 123);\n    printShortBoxed((short) 257);\n    printIntBoxed(1);\n    printIntBoxed(40_000);\n    printLongBoxed(123L);\n    printFloatBoxed(1.23F);\n    printDoubleBoxed(1.23);\n    printCharBoxed('Z');\n\n    printBoolean(Boolean.valueOf(\"true\"));\n    printByte(Byte.valueOf(\"123\"));\n    printShort(Short.valueOf(\"257\"));\n    printInt(Integer.valueOf(\"123\"));\n    printLong(Long.valueOf(\"123\"));\n    printFloat(Float.valueOf(\"1.23\"));\n    printDouble(Double.valueOf(\"1.23\"));\n    printChar(new Character('Z'));\n\n    printInt(getInteger());\n    printChar(getCharacter());\n\n    System.out.printf(\"%b, %d, %d, %d, %c, %d\", true, 1, 213, 40_000, 'c', 42L);\n    System.out.printf(\"%b, %d, %d, %d\", getBoolean(), getByte(), getShort(), getInt());\n\n    new TestPrimitives(false, (byte) 123, (short) 257, 40_000, 123L, 3.14f, 1.618, 'A');\n    new TestPrimitives('A', 1.618, 3.14f, 123L, 40_000, (short) 257, (byte) 123, false);\n    new TestPrimitives(Boolean.valueOf(\"false\"), Byte.valueOf(\"123\"), Short.valueOf(\"257\"), Integer.valueOf(\"40000\"), Long.valueOf(\"123\"),\n                       Float.valueOf(\"3.14\"), Double.valueOf(\"1.618\"), new Character('A'));\n  }\n\n  private TestPrimitives(boolean bool, byte b, short s, int i, long l, float f, double d, char c) {\n    System.out.printf(\"%b, %d, %d, %d, %d, %.2f, %.2f, %c\", bool, b, s, i, l, f, d, c);\n  }\n\n  private TestPrimitives(Character c, Double d, Float f, Long l, Integer i, Short s, Byte b, Boolean bool) {\n    System.out.printf(\"%b, %d, %d, %d, %d, %.2f, %.2f, %c\", bool, b, s, i, l, f, d, c);\n  }\n\n  public void printBoolean(boolean b) {\n    System.out.printf(\"%b\", b);\n  }\n\n  public void printByte(byte b) {\n    System.out.printf(\"%d\", b);\n  }\n\n  public void printShort(short s) {\n    System.out.printf(\"%d\", s);\n  }\n\n  public void printInt(int i) {\n    System.out.printf(\"%d\", i);\n  }\n\n  public void printLong(long l) {\n    System.out.printf(\"%d\", l);\n  }\n\n  public void printFloat(float f) {\n    System.out.printf(\"%f\", f);\n  }\n\n  public void printDouble(double d) {\n    System.out.printf(\"%f\", d);\n  }\n\n  public void printChar(char c) {\n    System.out.printf(\"%c\", c);\n  }\n\n\n  public void printBooleanBoxed(Boolean b) {\n    System.out.printf(\"%b\", b);\n  }\n\n  public void printByteBoxed(Byte b) {\n    System.out.printf(\"%d\", b);\n  }\n\n  public void printShortBoxed(Short s) {\n    System.out.printf(\"%d\", s);\n  }\n\n  public void printIntBoxed(Integer i) {\n    System.out.printf(\"%d\", i);\n  }\n\n  public void printLongBoxed(Long l) {\n    System.out.printf(\"%d\", l);\n  }\n\n  public void printFloatBoxed(Float f) {\n    System.out.printf(\"%f\", f);\n  }\n\n  public void printDoubleBoxed(Double d) {\n    System.out.printf(\"%f\", d);\n  }\n\n  public void printCharBoxed(Character c) {\n    System.out.printf(\"%c\", c);\n  }\n\n\n  public boolean getBoolean() {\n    return false;\n  }\n\n  public byte getByte() {\n    return (byte) 128;\n  }\n\n  public short getShort() {\n    return (short) 32768;\n  }\n\n  public int getInt() {\n    return 42;\n  }\n\n  public Integer getInteger() {\n    return 40_000;\n  }\n\n  public Character getCharacter() {\n    return 'Z';\n  }\n\n  public void printNarrowed() {\n    printByte((byte)getInt());\n    printShort((short)getInt());\n  }\n\n  public void constructor() {\n    new Byte((byte)1);\n  }\n\n  private boolean compare(char c) {\n    boolean res = (c > -1);\n    res = (c > 0);\n    res = (c > 1);\n    res = (c > '\\b');\n    res = (c > '\\t');\n    res = (c > '\\n');\n    res = (c > '\\f');\n    res = (c > '\\r');\n    res = (c > ' ');\n    res = (c > 'a');\n    res = (c > 'Z');\n    res = (c > 127);\n    res = (c > 255);\n    return res;\n  }\n\n  void testAutoBoxingCallRequired(boolean value) {\n    Boolean.valueOf(value).hashCode();\n  }\n\n  void testCastRequired() {\n    HashMap<String, Byte> map = new HashMap<String, Byte>();\n    map.put(\"test\", (byte) 0);\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestPrivateEmptyConstructor.java",
    "content": "package pkg;\n\npublic final class TestPrivateEmptyConstructor {\n   private TestPrivateEmptyConstructor() {}\n\n   public final void test() {\n\t\tSystem.out.println(\"test\");\n   }\n}"
  },
  {
    "path": "testData/src/pkg/TestShadowing.java",
    "content": "package pkg;\n\nclass TestShadowing extends TestShadowingSuperClass {\n  ext.Shadow.B instanceOfB = new ext.Shadow.B();\n  java.util.Calendar.Builder calBuilder = new java.util.Calendar.Builder();\n}\n\nclass TestShadowingSuperClass {\n  static class Calendar {\n    static class Builder { }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestSimpleInstanceOfRecordPatternJavac.java",
    "content": "package pkg;\n\npublic class TestSimpleInstanceOfRecordPatternJavac {\n  public static void main(String[] args) {\n\n  }\n\n  public static void instanceOfTest1(Object o) {\n    if (o instanceof R(Object s1)) {\n      System.out.println(s1);\n    }\n    System.out.println(\"1\");\n  }\n\n  public static void tryInstanceOfTest1(Object o) {\n    int a = 1;\n    try {\n      if (o instanceof R(String s1)) {\n        a += 34;\n      }\n    } catch (Exception e) {\n      if (o instanceof R(String s1)) {\n        a += 34;\n      }\n    }\n  }\n\n  public static void negativeInstanceOfTest1(Object o) {\n    if (!(o instanceof R(Object s1))) {\n      return;\n    } else if (s1.hashCode() == 1) {\n      System.out.println(s1);\n      System.out.println(\"1\");\n    }\n  }\n\n  public static void instanceOfTest2(Object o) {\n    if (o instanceof R(String s1)) {\n      System.out.println(s1);\n      if (s1.hashCode() == 1) {\n        System.out.println(\"1\");\n      }\n    }\n    System.out.println(\"5\");\n  }\n\n  public static void instanceOfTest3(Object o) {\n    if (o instanceof R(String s1)) {\n      if (s1.isEmpty()) {\n        System.out.println(\"111\");\n        System.out.println(\"111\");\n        System.out.println(\"111\");\n        System.out.println(\"111\");\n      }\n    }\n    System.out.println(\"s222222222222\");\n    System.out.println(\"s222222222222\");\n  }\n\n  public static void instanceOfTest4(Object o) {\n    if (o.hashCode() == 1) {\n      if (o instanceof R(String s1)) {\n        if (o instanceof R(String s2)) {\n          if (s1.isEmpty()) {\n            System.out.println(\"111\");\n            System.out.println(\"111\");\n            System.out.println(\"111\");\n            System.out.println(\"111\");\n          }\n        }\n      }\n    }\n    System.out.println(\"s222222222222\");\n    System.out.println(\"s222222222222\");\n  }\n\n  public static void instanceOfTestDouble1(Object o, Object o2) {\n    if (o instanceof R(Object s1)) {\n      System.out.println(s1);\n    }\n\n    if (o2 instanceof R(Object r)) {\n      System.out.println(r);\n    }\n    System.out.println(\"s2222222\");\n  }\n\n  public static void instanceOfTestDouble2(Object o, Object o2) {\n    if (o instanceof R(Object s1)) {\n      System.out.println(s1);\n    }\n\n    if (o2 instanceof R(String r)) {\n      System.out.println(r);\n    }\n    System.out.println(\"2222222\");\n  }\n\n  public static void instanceOfTestDoubleNegate2(Object o, Object o2) {\n    if (!(o instanceof R(Object s1))) {\n      return;\n    }\n    System.out.println(s1);\n\n    if (!(o2 instanceof R(String r))) {\n      return;\n    }\n    System.out.println(r);\n\n    System.out.println(\"2222222\");\n  }\n\n  record R(Object o) {\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestStaticNameClash.java",
    "content": "package pkg;\n\npublic class TestStaticNameClash {\n\n    public static String property;\n\n    public static void setProperty(final String property) {\n        TestStaticNameClash.property = property;\n    }\n\n}\n"
  },
  {
    "path": "testData/src/pkg/TestStringConcat.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestStringConcat {\n  public String test1(String prefix, int a) {\n    return prefix + a;\n  }\n\n  public String test2(String var, int b, Object c) {\n    return \"(\" + var + \"-\" + b + \"---\" + c + \")\";\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestStringLiterals.java",
    "content": "/*\n * Copyright 2000-2017 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\npublic class TestStringLiterals {\n  public static void main(String[] args) {\n    String a = \"abc\\b\\t\\n\\f\\r\\\"'\\\\def\";\n    char chars[] = {'\\b', '\\t', '\\n', '\\f', '\\r', '\"', '\\'', '\\\\'};\n    System.out.println(a);\n    System.out.println(chars);\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSuperInner.java",
    "content": "package pkg;\n\nclass TestSuperInner extends TestSuperInnerBase {\n  protected abstract class Inner2 extends Inner { }\n}"
  },
  {
    "path": "testData/src/pkg/TestSuperInnerBase.java",
    "content": "package pkg;\n\nclass TestSuperInnerBase {\n  protected abstract class Inner {\n    protected Inner() { }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestSuspendLambda.kt",
    "content": "package pkg\n\nval sl1: suspend () -> Unit = {\n    println(\"SL1\")\n}"
  },
  {
    "path": "testData/src/pkg/TestSwitchClassReferencesEcj.java",
    "content": "package pkg;\n\npublic class TestSwitchClassReferencesEcj{\n\n    public static void testObject(Object o) {\n        switch (o) {\n            case String s -> System.out.println(\"s\");\n            case Integer i -> System.out.println(\"i\");\n            case Object ob -> System.out.println(o);\n        }\n    }\n\n    public static void testObject2(Object o) {\n        switch (o) {\n            case String s -> System.out.println(\"s\");\n            case Integer i -> System.out.println(\"i\");\n            case Object ob -> System.out.println(o);\n            case null -> System.out.println(\"null\");\n        }\n    }\n\n    public static void testObject3(Object o) {\n        switch (o) {\n            case String s -> System.out.println(\"s\");\n            case Integer i -> System.out.println(\"i\");\n            case null -> System.out.println(\"null\");\n            default -> System.out.println(\"o\");\n        }\n    }\n\n    public static void testObject4(Object o) {\n        switch (o) {\n            case String s -> System.out.println(\"s\");\n            case Integer i -> System.out.println(\"i\");\n            default -> System.out.println(\"o\");\n        }\n    }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchClassReferencesFastExitEcj.java",
    "content": "package pkg;\n\npublic class TestSwitchClassReferencesFastExitEcj {\n    public static void testObject(Object o) {\n        Task:\n        while (true) {\n            for (int i = 0; i < o.hashCode(); i++) {\n                switch (o) {\n                    case String s:\n                        System.out.println(\"s\");\n                        System.exit(0);\n                        break;\n                    case Integer in:\n                        System.out.println(\"ii\");\n                        continue Task;\n                    case Object ob:\n                        System.out.println(\"s\");\n                        break Task;\n                }\n            }\n        }\n    }\n\n    public static void testObject2(Object o) {\n        Task:\n        while (true) {\n            for (int i = 0; i < o.hashCode(); i++) {\n                switch (o) {\n                    case String s -> {\n                        System.out.println(\"s\");\n                        System.exit(0);\n                        break;\n                    }\n                    case Integer in -> {\n                        System.out.println(\"ii\");\n                        continue Task;\n                    }\n                    case Object ob -> {\n                        System.out.println(\"s\");\n                        break Task;\n                    }\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchClassReferencesFastExitJavac.java",
    "content": "package pkg;\n\npublic class TestSwitchClassReferencesFastExitJavac {\n\n    public static void testObject(Object o) {\n        Task:\n        while (true) {\n            for (int i = 0; i < o.hashCode(); i++) {\n                switch (o) {\n                    case String s:\n                        System.out.println(\"s\");\n                        System.exit(0);\n                        break;\n                    case Integer in:\n                        System.out.println(\"ii\");\n                        continue Task;\n                    case Object ob:\n                        System.out.println(\"s\");\n                        break Task;\n                }\n            }\n        }\n    }\n\n    public static void testObject2(Object o) {\n        Task:\n        while (true) {\n            for (int i = 0; i < o.hashCode(); i++) {\n                switch (o) {\n                    case String s -> {\n                        System.out.println(\"s\");\n                        System.exit(0);\n                        break;\n                    }\n                    case Integer in -> {\n                        System.out.println(\"ii\");\n                        continue Task;\n                    }\n                    case Object ob -> {\n                        System.out.println(\"s\");\n                        break Task;\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchClassReferencesJavac.java",
    "content": "package pkg;\n\npublic class TestSwitchClassReferencesJavac {\n\n  public static void testObject(Object o) {\n    switch (o) {\n      case String s -> System.out.println(\"s\");\n      case Integer i -> System.out.println(\"i\");\n      case Object ob -> System.out.println(o);\n    }\n  }\n\n  public static void testObject2(Object o) {\n    switch (o) {\n      case String s -> System.out.println(\"s\");\n      case Integer i -> System.out.println(\"i\");\n      case Object ob -> System.out.println(o);\n      case null -> System.out.println(\"null\");\n    }\n  }\n\n  public static void testObject3(Object o) {\n    switch (o) {\n      case String s -> System.out.println(\"s\");\n      case Integer i -> System.out.println(\"i\");\n      case null -> System.out.println(\"null\");\n      default -> System.out.println(\"o\");\n    }\n  }\n\n  public static void testObject4(Object o) {\n    switch (o) {\n      case String s -> System.out.println(\"s\");\n      case Integer i -> System.out.println(\"i\");\n      default -> System.out.println(\"o\");\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchGuarded2Javac.java",
    "content": "package pkg;\n\npublic class TestSwitchGuarded2Javac {\n\n  public static void main(String[] args) {\n    testObject0(4);\n  }\n\n  public static void testObject0(Object o) {\n    try {\n      System.out.println(\"inside try 2\");\n      switch (o) {\n        case Integer n when n > 3 -> {\n          System.out.println(\"4\");\n          throw new RuntimeException();\n        }\n        case Object ob -> System.out.println(o);\n      }\n      System.out.println(\"2\");\n    } catch (UnsupportedOperationException e) {\n      System.out.println(\"exception\");\n    } finally {\n      System.out.println(\"finally\");\n    }\n  }\n\n  public static void testObject1(Object o) {\n    System.out.println(\"2\");\n    switch (o) {\n      case Integer n when n > 1 -> {\n        if (n == 1) {\n          System.out.println(\"212\");\n        }\n        System.out.println(2);\n        System.out.println(1);\n      }\n      case Integer n when n > 2 -> {\n        if (n == 1) {\n          System.out.println(\"4\");\n        }\n      }\n      case Integer n when n > 3 -> {\n        System.out.println(\"4\");\n      }\n      case Object ob -> System.out.println(o);\n    }\n    System.out.println(\"2\");\n    System.out.println(\"2\");\n    switch (o) {\n      case Integer n when n > 1 -> {\n        if (n == 1) {\n          System.out.println(\"212\");\n        }\n        System.out.println(2);\n        System.out.println(1);\n      }\n      case Integer n when n > 2 -> {\n        if (n == 1) {\n          System.out.println(\"4\");\n        }\n      }\n      case Integer n when n > 3 -> {\n        System.out.println(\"4\");\n      }\n      case Object ob -> System.out.println(o);\n    }\n    System.out.println(\"2\");\n  }\n\n\n  public static void testObject2(Object o) {\n    switch (o) {\n      case Integer n -> {\n        if (n == 1) {\n          System.out.println(\"2\");\n        }\n        System.out.println(1);\n        System.out.println(1);\n      }\n      case Object ob -> System.out.println(o);\n    }\n    System.out.println(\"2\");\n    switch (o) {\n      case Integer n -> {\n        if (n == 1) {\n          System.out.println(\"1\");\n        }\n        System.out.println(1);\n        System.out.println(1);\n      }\n      case Object ob -> System.out.println(o);\n    }\n    System.out.println(\"2\");\n  }\n\n  public static void testObject3(Object o) {\n    System.out.println(\"2\");\n    switch (o) {\n      case Integer n when n > 1 -> {\n        if (n == 1) {\n          System.out.println(\"212\");\n        }\n        System.out.println(2);\n        System.out.println(1);\n      }\n      case Integer n when n > 2 -> {\n        if (n == 1) {\n          System.out.println(\"4\");\n        }\n      }\n      case Integer n when n > 3 -> {\n        System.out.println(\"4\");\n      }\n      case Object ob -> System.out.println(o);\n    }\n    System.out.println(\"2\");\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchGuardedEcj.java",
    "content": "package pkg;\n\npublic class TestSwitchGuardedEcj {\n\n  public static void main(String[] args) {\n\n  }\n\n  public static void testObject(Object o) {\n    switch (o) {\n      case String s when s.isEmpty() && s.getBytes().length == 2 -> System.out.println(\"empty s\");\n      case String s -> System.out.println(\"s\");\n      case Integer i -> System.out.println(\"i\");\n      case Object ob -> System.out.println(o);\n    }\n    System.out.println(\"1\");\n  }\n\n  public static void testObject2(Object o) {\n    switch (o) {\n      case String s when s.isEmpty() && s.getBytes().length == 2 -> System.out.println(\"empty s\");\n      case String s -> System.out.println(\"s\");\n      case Integer i -> System.out.println(\"ii\");\n      case Object ob -> System.out.println(o);\n    }\n  }\n\n  public static void testObject3(Object o) {\n    TASK:\n    while (o.hashCode() == 1) {\n      switch (o) {\n        case String s when s.isEmpty() && s.getBytes().length == 2:\n          System.out.println(\"empty s\");\n          break TASK;\n        case String s:\n          System.out.println(\"s\");\n          continue TASK;\n        case Integer i:\n          System.out.println(\"i\");\n          break;\n        case Object ob:\n          System.out.println(o);\n          break;\n      }\n      break;\n    }\n    System.out.println(\"1\");\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchGuardedJavac.java",
    "content": "package pkg;\n\npublic class TestSwitchGuardedJavac {\n\n  public static void main(String[] args) {\n\n  }\n\n  public static void testObject(Object o) {\n    switch (o) {\n      case String s when s.isEmpty() && s.getBytes().length == 2 -> System.out.println(\"empty s\");\n      case String s -> System.out.println(\"s\");\n      case Integer i -> System.out.println(\"iii\");\n      case Object ob -> System.out.println(o);\n    }\n    System.out.println(\"1\");\n  }\n\n  public static void testObject2(Object o) {\n    switch (o) {\n      case String s when s.isEmpty() && s.getBytes().length == 2 -> System.out.println(\"empty s\");\n      case String s -> System.out.println(\"s\");\n      case Integer i -> System.out.println(\"ii\");\n      case Object ob -> System.out.println(o);\n    }\n  }\n\n  public static void testObject3(Object o) {\n    TASK:\n    while (o.hashCode() == 1) {\n      switch (o) {\n        case String s when s.isEmpty() && s.getBytes().length == 2:\n          System.out.println(\"empty s\");\n          break TASK;\n        case String s:\n          System.out.println(\"s\");\n          continue TASK;\n        case Integer i:\n          System.out.println(\"i\");\n          break;\n        case Object ob:\n          System.out.println(o);\n          break;\n      }\n      break;\n    }\n    System.out.println(\"1\");\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchNestedDeconstructionsJavac.java",
    "content": "package pkg;\n\npublic class TestSwitchNestedDeconstructionsJavac {\n  public static void main(String[] args) {\n\n  }\n\n  record R1(Object o) {\n\n  }\n\n\n  public static void testNestedSwitches(Object o) {\n    switch (o) {\n      case R2(String s, String s2) when s.length() > 123456789 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(String s, String s2) when s.length() > 2 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(String s, Object s2) when s.length() > 45 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(Object s, Object s2) when s.hashCode() > 7 -> {\n        if (s.hashCode() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(Object s, Object s2) -> {\n        if (s.hashCode() == 2) {\n          System.out.println(o);\n        }\n      }\n      case Object ob -> System.out.println(o);\n    }\n  }\n\n  public static void testStringString(Object o) {\n    switch (o) {\n      case R2(String s1, String s2) -> {\n        if (s2.isEmpty()) {\n          System.out.println(\"3\");\n        }\n      }\n      default -> System.out.println(\"3\");\n    }\n    System.out.println(\"1\");\n  }\n\n  public static void testNestedLevel2(Object o) {\n    switch (o) {\n      case R2(String s, String s2) when s2.length() > 11 -> {\n        if (s.length() == 9) {\n          System.out.println(o);\n        }\n      }\n      case R2(Object s, R1(String s2)) when s2.length() > 7 -> {\n        if (s.hashCode() == 2) {\n          System.out.println(o);\n        }\n      }\n      case Object ob -> System.out.println(o);\n    }\n  }\n\n  public static void testNumberString(Object o) {\n    switch (o) {\n      case R2(String s, String s2) when s2.length() > 10 -> {\n        if (s.length() == 9) {\n          System.out.println(o);\n        }\n      }\n      case R2(Number s, String s2) when s2.length() > 9 -> {\n        if (s.hashCode() == 9) {\n          System.out.println(o);\n        }\n      }\n      case R2(String s, Object s2) when s.length() > 7 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case Object ob -> System.out.println(o);\n    }\n  }\n\n  public static void test2DeepDeconstruction(Object o) {\n    switch (o) {\n      case R1(R1(String s)) when s.hashCode() == 5 -> {\n        System.out.println(\"123456789\");\n      }\n      case R1(String s) when s.hashCode() == 3 -> {\n        System.out.println(\"3\");\n      }\n      default -> System.out.println(\"3\");\n    }\n    System.out.println(\"1\");\n  }\n\n  public static void testDoubleLongCase(Object o) {\n    switch (o) {\n      case R2(String s, String s2) when s.length() > 3 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(String s, String s2) when s.length() > 4 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(String s, Object s2) when s.length() > 3 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(Object s, Object s2) when s.hashCode() > 3 -> {\n        if (s.hashCode() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(Object s, Object s2) -> {\n        if (s.hashCode() == 2) {\n          System.out.println(o);\n        }\n      }\n      case Object ob -> System.out.println(o);\n    }\n\n    switch (o) {\n      case R2(String s, String s2) when s.length() > 3 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(String s, String s2) when s.length() > 4 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(String s, Object s2) when s.length() > 3 -> {\n        if (s.length() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(Object s, Object s2) when s.hashCode() > 3 -> {\n        if (s.hashCode() == 2) {\n          System.out.println(o);\n        }\n      }\n      case R2(Object s, Object s2) -> {\n        if (s.hashCode() == 2) {\n          System.out.println(o);\n        }\n      }\n      case Object ob -> System.out.println(o);\n    }\n  }\n\n  record R2(Object o, Object o2) {\n\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestSwitchOnEnum.java",
    "content": "package pkg;\n\nimport java.util.concurrent.TimeUnit;\n\n/**\n * This illustrates a bug in fernflower as of 20170421. Decompiled output of this class does not compile back.\n */\npublic class TestSwitchOnEnum {\n\n  int myInt;\n\n  public int testSOE(TimeUnit t) {\n    // This creates anonymous SwitchMap inner class.\n    switch (t) {\n      case MICROSECONDS:\n        return 2;\n      case SECONDS:\n        return 1;\n    }\n    return 0;\n  }\n\n  static class Example {\n\n    enum A { A1, A2}\n\n    enum B { B1, B2}\n\n    void test(A a, B b){\n      switch (a){\n        case A1:\n          System.out.println(\"A1\");\n          break;\n        case A2:\n          System.out.println(\"A2\");\n          break;\n      }\n      switch (b){\n        case B1:\n          System.out.println(\"B1\");\n          break;\n        case B2:\n          System.out.println(\"B2\");\n          break;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchOnEnumEclipse.java",
    "content": "package pkg;\n\nimport java.util.concurrent.TimeUnit;\n\npublic class TestSwitchOnEnumEclipse {\n\n  int myInt;\n\n  public int testSOE(TimeUnit t) {\n    switch (t) {\n      case MICROSECONDS:\n        return 2;\n      case SECONDS:\n        return 1;\n    }\n    return 0;\n  }\n\n  static class Example {\n\n    enum A { A1, A2 }\n\n    enum B { B1, B2 }\n\n    void test(A a, B b) {\n      switch (a) {\n        case A1:\n          System.out.println(\"A1\");\n          break;\n        case A2:\n          System.out.println(\"A2\");\n          break;\n      }\n      switch (b) {\n        case B1:\n          System.out.println(\"B1\");\n          break;\n        case B2:\n          System.out.println(\"B2\");\n          break;\n      }\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestSwitchOnStringsEcj.java",
    "content": "package pkg;\n\npublic class TestSwitchOnStringsEcj {\n    String s;\n    static final String S = \"\";\n\n    void noCase() {\n        switch (getStr()) {\n        }\n    }\n\n    void oneCase(String s) {\n        System.out.println(1);\n        switch (s) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n        }\n        System.out.println(3);\n    }\n\n    void oneCaseWithDefault() {\n        System.out.println(1);\n        switch (s) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            default:\n                System.out.println(3);\n                break;\n        }\n        System.out.println(4);\n    }\n\n    void multipleCases1() {\n        System.out.println(1);\n        switch (S) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            case \"yyy\":\n                System.out.println(3);\n                break;\n        }\n        System.out.println(4);\n    }\n\n    void multipleCasesWithDefault1() {\n        System.out.println(1);\n        switch (getStr()) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            case \"yyy\":\n                System.out.println(3);\n                break;\n            default:\n                System.out.println(4);\n                break;\n        }\n        System.out.println(5);\n    }\n\n    void multipleCases2() {\n        System.out.println(1);\n        switch (S) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            case \"yyy\":\n                System.out.println(3);\n                break;\n            case \"zzz\":\n                System.out.println(4);\n                break;\n        }\n        System.out.println(5);\n    }\n\n    void multipleCasesWithDefault2() {\n        System.out.println(1);\n        switch (getStr()) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            case \"yyy\":\n                System.out.println(3);\n                break;\n            case \"zzz\":\n                System.out.println(4);\n                break;\n            default:\n                System.out.println(5);\n                break;\n        }\n        System.out.println(6);\n    }\n\n    // todo support cases with several case label elements\n    //void combined() {\n    //    System.out.println(\"started\");\n    //    if (s.length() > 0) {\n    //        System.out.println();\n    //        switch(s) {\n    //            case \"b\" -> System.out.println(1);\n    //            case \"d\" -> System.out.println(2);\n    //            case \"a\" -> System.out.println(3);\n    //            case \"f\" -> System.out.println(4);\n    //            default -> System.out.println(Math.random());\n    //        }\n    //        System.out.println(s);\n    //        combined();\n    //    } else {\n    //        try {\n    //            switch (getStr()) {\n    //                case \"h\":\n    //                case \"i\":\n    //                    while (s != null) {\n    //                        try {\n    //                            if (s.length() == 1) {\n    //                                System.out.println(s);\n    //                            }\n    //                        } catch (NullPointerException e) {\n    //                            System.out.println(e.getMessage());\n    //                        }\n    //                    }\n    //                    System.out.println(5);\n    //                case \"j\":\n    //                case \"f\":\n    //                    System.out.println(6);\n    //                    return;\n    //                default:\n    //                    System.out.println(7);\n    //            }\n    //        } catch (NullPointerException e) {\n    //            for (int i = 0; i < 10; i++) {\n    //                switch (getStr()) {\n    //                    case S -> System.out.println(8);\n    //                    default -> System.out.println(e.getMessage());\n    //                }\n    //            }\n    //            System.out.println(9);\n    //        }\n    //    }\n    //    System.out.println(\"finished\");\n    //}\n\n    String getStr() {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchOnStringsJavac.java",
    "content": "package pkg;\npublic class TestSwitchOnStringsJavac {\n    String s;\n    static final String S = \"\";\n\n    void noCase() {\n        switch (getStr()) {\n        }\n    }\n\n    void oneCase(String s) {\n        System.out.println(1);\n        switch (s) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n        }\n        System.out.println(3);\n    }\n\n    void oneCaseWithDefault() {\n        System.out.println(1);\n        switch (s) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            default:\n                System.out.println(3);\n                break;\n        }\n        System.out.println(4);\n    }\n\n    void multipleCases1() {\n        System.out.println(1);\n        switch (S) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            case \"yyy\":\n                System.out.println(3);\n                break;\n        }\n        System.out.println(4);\n    }\n\n    void multipleCasesWithDefault1() {\n        System.out.println(1);\n        switch (getStr()) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            case \"yyy\":\n                System.out.println(3);\n                break;\n            default:\n                System.out.println(4);\n                break;\n        }\n        System.out.println(5);\n    }\n\n    void multipleCases2() {\n        System.out.println(1);\n        switch (S) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            case \"yyy\":\n                System.out.println(3);\n                break;\n            case \"zzz\":\n                System.out.println(4);\n                break;\n        }\n        System.out.println(5);\n    }\n\n    void multipleCasesWithDefault2() {\n        System.out.println(1);\n        switch (getStr()) {\n            case \"xxx\":\n                System.out.println(2);\n                break;\n            case \"yyy\":\n                System.out.println(3);\n                break;\n            case \"zzz\":\n                System.out.println(4);\n                break;\n            default:\n                System.out.println(5);\n                break;\n        }\n        System.out.println(6);\n    }\n\n\n    void combined() {\n        System.out.println(\"started\");\n        if (s.length() > 0) {\n            System.out.println();\n            switch(s) {\n                case \"b\" -> System.out.println(1);\n                case \"d\" -> System.out.println(2);\n                case \"a\" -> System.out.println(3);\n                case \"f\" -> System.out.println(4);\n                default -> System.out.println(Math.random());\n            }\n            System.out.println(s);\n            combined();\n        } else {\n            try {\n                switch (getStr()) {\n                    case \"h\":\n                    case \"i\":\n                        while (s != null) {\n                            try {\n                                if (s.length() == 1) {\n                                    System.out.println(s);\n                                }\n                            } catch (NullPointerException e) {\n                                System.out.println(e.getMessage());\n                            }\n                        }\n                        System.out.println(5);\n                    case \"j\":\n                    case \"f\":\n                        System.out.println(6);\n                        return;\n                    default:\n                        System.out.println(7);\n                }\n            } catch (NullPointerException e) {\n                for (int i = 0; i < 10; i++) {\n                    switch (getStr()) {\n                        case S -> System.out.println(8);\n                        default -> System.out.println(e.getMessage());\n                    }\n                }\n                System.out.println(9);\n            }\n        }\n        System.out.println(\"finished\");\n    }\n\n    String getStr() {\n        return \"\";\n    }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSwitchRules.java",
    "content": "package pkg;\n\npublic class TestSwitchRules {\n  public static void main(String[] args) {\n    test1(\"1\");\n    test2(\"1\");\n    test3(\"1\");\n  }\n\n\n  private static void test1(String r2) {\n    switch (r2) {\n      case \"2\" -> System.out.println(\"4\");\n      case \"3\" -> System.out.println(\"2\");\n      default -> System.out.println(\"31\");\n    }\n  }\n\n  private static void test2(String r2) {\n    switch (r2) {\n      case \"2\":\n        break;\n      default:\n        System.out.println(\"31\");\n        break;\n      case \"3\":\n        System.out.println(\"2\");\n        break;\n    }\n  }\n\n  private static void test3(String r2) {\n    switch (r2) {\n      case \"2\":\n      default:\n        System.out.println(\"31\");\n        break;\n      case \"3\":\n        System.out.println(\"2\");\n        break;\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestSwitchSimpleReferencesJavac.java",
    "content": "package pkg;\n\npublic class TestSwitchSimpleReferencesJavac {\n\n  private static void testString(String r2) {\n    switch (r2) {\n      case \"first\":\n        System.out.println(\"1\");\n        break;\n      case \"second\":\n        System.out.println(\"2\");\n      case \"third\":\n        System.out.println(\"3\");\n        break;\n      case null, default:\n        System.out.println(\"default null\");\n    }\n  }\n\n  private static void testString2(String r2) {\n    switch (r2) {\n      case \"first\":\n        System.out.println(\"1\");\n        break;\n      case \"second\":\n        System.out.println(\"2\");\n        break;\n      case String s:\n        System.out.println(s);\n    }\n  }\n\n  private static void testString3(String r2) {\n    switch (r2) {\n      case \"first\":\n        System.out.println(\"1\");\n        break;\n      case \"second\":\n        System.out.println(\"2\");\n        break;\n      case null:\n        System.out.println(\"null\");\n        break;\n      case String s:\n        System.out.println(s);\n    }\n  }\n\n\n  private static void testByte(Byte r2) {\n    switch (r2) {\n      case 1:\n        break;\n      case 2:\n        System.out.println(\"2\");\n        break;\n      case null:\n      default:\n        System.out.println(\"default, null\");\n    }\n  }\n\n  private static void testChar(Character r2) {\n    switch (r2) {\n      case '1':\n      case '2':\n        System.out.println(\"2\");\n        break;\n      case null:\n      default:\n        System.out.println(\"default, null\");\n    }\n  }\n\n\n  private static void testInt(Integer r2) {\n    switch (r2) {\n      case 1:\n        break;\n      case 3:\n        System.out.println(\"2\");\n        break;\n      case null:\n      default:\n        System.out.println(\"default, null\");\n    }\n  }\n\n  private static void testInt2(Integer r2) {\n    switch (r2) {\n      case 1:\n        break;\n      case 3:\n        System.out.println(\"2\");\n        break;\n      default:\n        System.out.println(\"default\");\n    }\n  }\n\n  enum Numbers {\n    FIRST, SECOND\n  }\n\n  private static void testEnum(Numbers r2) {\n    switch (r2) {\n      case Numbers.FIRST:\n        break;\n      case SECOND:\n        System.out.println(\"2\");\n        break;\n      case null:\n        System.out.println(\"null\");\n    }\n  }\n\n  private static void testEnum2(Numbers r2) {\n    switch (r2) {\n      case Numbers.FIRST:\n        break;\n      case SECOND:\n        System.out.println(\"2\");\n        break;\n      case null:\n        System.out.println(\"null\");\n    }\n  }\n\n  private static void testEnum3(Numbers r2) {\n    switch (r2) {\n      case Numbers.FIRST:\n        break;\n      case SECOND:\n        System.out.println(\"2\");\n        break;\n      case null:\n        System.out.println(\"null\");\n        break;\n      case Numbers n:\n        System.out.println(n);\n    }\n  }\n\n  private static void testEnum4(Numbers r2) {\n    switch (r2) {\n      case Numbers.FIRST:\n        break;\n      case SECOND:\n        System.out.println(\"2\");\n        break;\n      default:\n        System.out.println(\"default\");\n    }\n  }\n\n  private static void testEnum5(Numbers r2) {\n    switch (r2) {\n      case SECOND:\n        System.out.println(\"2\");\n        break;\n      case null, default:\n        System.out.println(\"default\");\n    }\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestSwitchWithDeconstructionsWithoutNestedJavac.java",
    "content": "package pkg;\n\npublic class TestSwitchWithDeconstructionsWithoutNestedJavac {\n    public static void main(String[] args) {\n\n    }\n\n  record R1(Object o, Object o2) {\n\n  }\n\n  public static void testStringString(Object o) {\n        switch (o) {\n            case R1(String s1, String s2) -> {\n                if (s2.isEmpty()) {\n                    System.out.println(\"2\");\n                }\n            }\n            default -> System.out.println(\"3\");\n        }\n        System.out.println(\"1\");\n    }\n\n    public static void testStringObjectWhen(Object o) {\n        switch (o) {\n            case R1(String s1, Object s2) when s1.hashCode() == 3 -> {\n                if (s1.hashCode() == 1) {\n                    System.out.println(\"2\");\n                    System.out.println(\"2\");\n                    System.out.println(\"2\");\n                }\n            }\n            default -> System.out.println(\"3\");\n        }\n        System.out.println(\"1\");\n    }\n\n    public static void testStringObject(Object o) {\n        switch (o) {\n            case R1(String s1, Object s2) -> {\n                if (s1.isEmpty()) {\n                    System.out.println(\"1\");\n                }\n            }\n            default -> System.out.println(\"3\");\n        }\n        System.out.println(\"1\");\n    }\n\n    public static void testObjectString(Object o) {\n        switch (o) {\n            case R1(Object s1, String s2) -> {\n                if (s1.hashCode() == 1) {\n                    System.out.println(\"1\");\n                }\n            }\n            default -> System.out.println(\"3\");\n        }\n        System.out.println(\"1\");\n    }\n\n    public static void testObjectObject(Object o) {\n        switch (o) {\n            case R1(Object s1, Object s2) -> {\n                if (s1.hashCode() == 1) {\n                    System.out.println(\"1\");\n                }\n            }\n            default -> System.out.println(\"3\");\n        }\n        System.out.println(\"1\");\n    }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSynchronizedMapping.java",
    "content": "package pkg;\n\nimport java.lang.Override;\nimport java.lang.Runnable;\n\npublic class TestSynchronizedMapping {\n  public int test(int a) {\n    synchronized (this) {\n      a++;\n    }\n    return a++;\n  }\n\n  public void test2(String a) {\n    System.out.println(a);\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestSynchronizedUnprotected.kt",
    "content": "package pkg\n\nclass TestSynchronizedUnprotected {\n    fun test() {\n        synchronized(this) {\n            println(\"Boom\")\n        }\n    }\n}"
  },
  {
    "path": "testData/src/pkg/TestSyntheticAccess.java",
    "content": "package pkg;\n\nclass TestSyntheticAccess {\n\n  private static int s;\n  private int i;\n\n  private class Incrementer {\n    void orI() {\n      i|=1;\n    }\n\n    void incrementI() {\n      i++;\n    }\n\n    void decrementI() {\n      --i;\n    }\n\n    void incrementS() {\n      ++s;\n    }\n\n    void decrementS() {\n      s--;\n    }\n  }\n\n  private class Assigner {\n    void assignI(int newValue) {\n      i = newValue;\n    }\n\n    void assignS(int newValue) {\n      s = newValue;\n    }\n  }\n\n}\n"
  },
  {
    "path": "testData/src/pkg/TestThrowException.java",
    "content": "package pkg;\n\nimport java.lang.Override;\nimport java.lang.Runnable;\n\npublic class TestThrowException {\n  Runnable r;\n  public TestThrowException(int a) {\n    if (a > 0) {\n      throw new IllegalArgumentException(\"xxx\");\n    }\n    r = new Runnable() {\n      @Override\n      public void run() {\n        int a = 5;\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestTryCatchFinally.java",
    "content": "/*\n * Copyright 2000-2014 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.lang.Exception;\nimport java.lang.RuntimeException;\n\npublic class TestTryCatchFinally {\n  public void test1(String x) {\n    try {\n      System.out.println(\"sout1\");\n    } catch (Exception e) {\n      try {\n        System.out.println(\"sout2\");\n      } catch (Exception e2) {\n        // Empty\n        // Empty\n        // Empty\n      }\n    } finally {\n      System.out.println(\"finally\");\n    }\n  }\n\n  int foo(int a) throws Exception {\n    if (a < 1) {\n      throw new RuntimeException();\n    } else if ( a <5) {\n      return a;\n    }\n    else {\n      throw new Exception();\n    }\n  }\n\n  public int test(String a) {\n    try {\n      return Integer.parseInt(a);\n    } catch (Exception e) {\n      System.out.println(\"Error\" + e);\n    } finally {\n      System.out.println(\"Finally\");\n    }\n    return -1;\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestTryCatchFinallyJsrRet.java",
    "content": "/*\n * Copyright 2000-2021 JetBrains s.r.o.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage pkg;\n\nimport java.lang.Exception;\nimport java.lang.RuntimeException;\n\n/**\n *\n * This test case is intended to test {@code jsr}/{@code ret} instructions handling.\n * Above mentioned instructions was allowed in class files until version 51.0 (Java 7) and was used\n * to implement {@code finally} block. More details can be found in JVMS11 &sect;3.13 \"Compiling finally\".\n *\n * It is vital for this class file to be compiled with javac version that employ {@code jsr}/{@code ret} instructions\n * for {@code finally} block implementation.\n *\n * Javac from Oracle JDK 1.3.1_28 for Windows was used to comple this file.\n *\n */\npublic class TestTryCatchFinallyJsrRet {\n  public void test1(String x) {\n    try {\n      System.out.println(\"sout1\");\n    } catch (Exception e) {\n      try {\n        System.out.println(\"sout2\");\n      } catch (Exception e2) {\n        // Empty\n        // Empty\n        // Empty\n      }\n    } finally {\n      System.out.println(\"finally\");\n    }\n  }\n\n  int foo(int a) throws Exception {\n    if (a < 1) {\n      throw new RuntimeException();\n    } else if ( a < 5) {\n      return a;\n    }\n    else {\n      throw new Exception();\n    }\n  }\n\n  public int test(String a) {\n    try {\n      return Integer.parseInt(a);\n    } catch (Exception e) {\n      System.out.println(\"Error\" + e);\n    } finally {\n      System.out.println(\"Finally\");\n    }\n    return -1;\n  }\n}"
  },
  {
    "path": "testData/src/pkg/TestUnionType.java",
    "content": "package pkg;\n\nimport java.io.Serializable;\nimport java.util.Comparator;\n\npublic interface TestUnionType {\n  public static Comparator comparingInt() {\n    return (Comparator & Serializable)(c1, c2) -> 1;\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TestVarArgCalls.java",
    "content": "package pkg;\n\npublic class TestVarArgCalls {\n  public void doSmth() {\n    printAll(\"Test\");\n    printAll(\"Test: %s\", \"abc\");\n    printAll(\"Test: %s - %s\", \"abc\", \"DEF\");\n\n    printComplex(\"Test\");\n    printComplex(\"Test: %[0]s\", new String[] { \"abc\" });\n    printComplex(\"Test: %[0]s - %[0]s\", new String[] { \"abc\" }, new String[] { \"DEF\" });\n\n    String.format(\"Test\");\n    String.format(\"Test: %d\", 123);\n    String.format(\"Test: %d - %s\", 123, \"DEF\");\n\n    Object[] data = { \"Hello\" };\n    String.format(\"Test: %s\", (Object) data);\n    String.format(\"Test: %s\", (Object[]) data);\n  }\n\n  public void printAll(String fmt, String... params) {\n    System.out.println(String.format(fmt, (Object[]) params));\n  }\n\n  public void printComplex(String fmt, String[]... params) {\n    System.out.println(String.format(fmt, (Object[]) params));\n  }\n}\n"
  },
  {
    "path": "testData/src/pkg/TryToPreserveCast.java",
    "content": "import java.nio.Buffer;\nimport java.nio.ByteBuffer;\n\npublic class TryToPreserveCast {\n    public TryToPreserveCast() {\n    }\n\n    public static void main(String[] args) {\n\n    }\n\n    public void test(ByteBuffer buffer) {\n        ((Buffer) buffer).limit(1);\n        (buffer).limit(2);\n    }\n}"
  },
  {
    "path": "testData/src/pkg/TypeAnnotations.java",
    "content": "package pkg;\n\nimport java.lang.annotation.*;\nimport java.util.*;\n\nclass TypeAnnotations {\n  @Target(ElementType.TYPE_USE)\n  @interface TA { String value(); }\n\n  @Target({ElementType.FIELD, ElementType.TYPE_USE})\n  @interface MixA { String value(); }\n\n  private @TA(\"field type\") String f1;\n\n  private @MixA(\"field and type\") String f2;\n\n  @TA(\"return type\") int m1() {\n    return 42;\n  }\n\n  void m2(@TA(\"parameter\") int i) { }\n}"
  },
  {
    "path": "testData/src/pkg/package-info.java",
    "content": "@PkgAnno(\"...\")\npackage pkg;\n\nimport ext.PkgAnno;\n"
  },
  {
    "path": "testData/src/records/TestRecordAnno.java",
    "content": "package records;\n\nimport java.lang.annotation.*;\n\npublic record TestRecordAnno(@TA @RC int x, @M @P int y) {}\n\n@Target(ElementType.TYPE_USE)\n@interface TA {}\n\n@Target(ElementType.RECORD_COMPONENT)\n@interface RC {}\n\n@Target(ElementType.METHOD)\n@interface M {}\n\n@Target(ElementType.PARAMETER)\n@interface P {}\n"
  },
  {
    "path": "testData/src/records/TestRecordEmpty.java",
    "content": "package records;\n\npublic record TestRecordEmpty() {\n  public int hashCode() {\n    return 0;\n  }\n}"
  },
  {
    "path": "testData/src/records/TestRecordGenericVararg.java",
    "content": "package records;\n\npublic record TestRecordGenericVararg<T>(T first, T... other) {\n  @SafeVarargs\n  public TestRecordGenericVararg {}\n}"
  },
  {
    "path": "testData/src/records/TestRecordSimple.java",
    "content": "package records;\n\npublic record TestRecordSimple(int x, int y) {}"
  },
  {
    "path": "testData/src/records/TestRecordVararg.java",
    "content": "package records;\n\npublic record TestRecordVararg(int x, int[]... y) {}"
  },
  {
    "path": "testData/src/sealed/ClassExtends.java",
    "content": "package sealed;\n\nfinal class ClassExtends extends RootWithClassOuter {\n}"
  },
  {
    "path": "testData/src/sealed/ClassImplements.java",
    "content": "package sealed;\n\nfinal class ClassImplements implements RootWithInterfaceOuter {\n}\n"
  },
  {
    "path": "testData/src/sealed/ClassNonSealed.java",
    "content": "package sealed;\n\nnon-sealed class ClassNonSealed extends RootWithClassOuter implements RootWithInterfaceInnerAndOuter{\n}"
  },
  {
    "path": "testData/src/sealed/ClassNonSealedExtendsImplements.java",
    "content": "package sealed;\n\nnon-sealed class ClassNonSealedExtendsImplements extends RootWithClassOuter implements RootWithInterfaceOuter {\n}"
  },
  {
    "path": "testData/src/sealed/EnumWithOverride.java",
    "content": "package sealed;\n\nenum EnumWithOverride {\n\tFOO {\n\t};\n}"
  },
  {
    "path": "testData/src/sealed/InterfaceNonSealed.java",
    "content": "package sealed;\n\nnon-sealed interface InterfaceNonSealed extends RootWithInterfaceOuter {\n}"
  },
  {
    "path": "testData/src/sealed/RootWithClassInner.java",
    "content": "package sealed;\n\nsealed class RootWithClassInner {\n  static final class Inner extends RootWithClassInner {\n  }\n}"
  },
  {
    "path": "testData/src/sealed/RootWithClassOuter.java",
    "content": "package sealed;\n\nabstract sealed class RootWithClassOuter permits ClassExtends, ClassNonSealed, ClassNonSealedExtendsImplements {\n}"
  },
  {
    "path": "testData/src/sealed/RootWithInterfaceInner.java",
    "content": "package sealed;\n\nsealed interface RootWithInterfaceInner {\n  final class Inner implements RootWithInterfaceInner {\n  }\n}"
  },
  {
    "path": "testData/src/sealed/RootWithInterfaceInnerAndOuter.java",
    "content": "package sealed;\n\nsealed interface RootWithInterfaceInnerAndOuter permits RootWithInterfaceInnerAndOuter.Inner, ClassNonSealed {\n  final class Inner implements RootWithInterfaceInnerAndOuter {\n  }\n}"
  },
  {
    "path": "testData/src/sealed/RootWithInterfaceOuter.java",
    "content": "package sealed;\n\nsealed interface RootWithInterfaceOuter permits ClassImplements, InterfaceNonSealed, ClassNonSealedExtendsImplements {\n}"
  },
  {
    "path": "testData/src/sealed/bar/BarClassExtends.java",
    "content": "package sealed.bar;\n\nimport sealed.foo.RootWithModule;\n\nfinal public class BarClassExtends extends RootWithModule {\n}"
  },
  {
    "path": "testData/src/sealed/foo/RootWithModule.java",
    "content": "package sealed.foo;\n\nimport sealed.bar.BarClassExtends;\n\npublic abstract sealed class RootWithModule permits BarClassExtends {\n}"
  },
  {
    "path": "testData/src/typeAnnotations/A.java",
    "content": "package typeAnnotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE_USE)\npublic @interface A {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/ArrayNestedTypeAnnotations.java",
    "content": "package typeAnnotations;\n\npublic class ArrayNestedTypeAnnotations {\n    @A Z.Y.X.W[] w1;\n    Z.@B Y.X.W[] @E [] w2;\n    Z.Y.@C X.W @F [] @A [] @B [] w3;\n    Z.Y.X.@D W @D [] w4;\n    @A Z.@B Y.@C X.@D W[][] w5;\n    @L Z. Y.X.@L W[] @L [] w6;\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/ArrayTypeAnnotations.java",
    "content": "package typeAnnotations;\n\npublic class ArrayTypeAnnotations implements ParentInterface {\n    @A String[] s1 = new String[0];\n    String @B [] s2 = new String[0];\n    String @C [][] s3 = new String[0][0];\n    String [] @D [] s4 = new String[0][0];\n    @A String[] s5() { return null; }\n    String @B [] s6() { return null; }\n    @A String @B [] @C [] @D [] s7() { return null; }\n    @L String @L [][] @L [] s8() { return null; }\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/B.java",
    "content": "package typeAnnotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE_USE)\npublic @interface B {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/Bar.java",
    "content": "package typeAnnotations;\n\npublic interface Bar {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/BarGeneric.java",
    "content": "package typeAnnotations;\n\npublic interface BarGeneric<T, U> {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/C.java",
    "content": "package typeAnnotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE_USE)\npublic @interface C {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/ClassSuperTypeAnnotations.java",
    "content": "package typeAnnotations;\n\npublic class ClassSuperTypeAnnotations extends @L @A Foo implements @B Bar, @B BarGeneric<@F String, @L @A String @B []> {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/D.java",
    "content": "package typeAnnotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE_USE)\npublic @interface D {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/E.java",
    "content": "package typeAnnotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE_USE)\npublic @interface E {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/F.java",
    "content": "package typeAnnotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE_USE)\npublic @interface F {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/Foo.java",
    "content": "package typeAnnotations;\n\npublic abstract class Foo {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/GenericArrayNestedTypeAnnotations.java",
    "content": "package typeAnnotations;\n\npublic class GenericArrayNestedTypeAnnotations {\n    @A V.U<String>.T<Boolean, Integer, Float> @A [] t1;\n    V.@B U<String>.T<Boolean, Integer, Float>[] t2;\n    V.U<String>.@C T<Boolean, Integer, Float> @B [] @D [] t3;\n    V.U<@D String>.T<Boolean, Integer, Float> @F [] t4;\n    @B V.@A U<@A String>.@A T<@E Boolean, @F Integer, Float>[] t5;\n    @L V.U<String>.T<Boolean, Integer, Float>[][] t6;\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/GenericArrayTypeAnnotations.java",
    "content": "package typeAnnotations;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class GenericArrayTypeAnnotations {\n    @A List<Comparable<Object[][][]>> l1;\n    List<@B Comparable<Object[][][]>> l2;\n    List<Comparable<@F Object[][][]>> l3;\n    List<Comparable<Object @C [][][]>> l4;\n    List<Comparable<Object[] @D [][]>> l5;\n    List<Comparable<Object[][] @E []>> l6;\n    @A List<@B Comparable<@F Object @C [] @D [] @E []>> l7;\n    @A List<@A Comparable<@A Object @A [] @A [] @A []>> l8;\n    Map<Object @A @B [][], List<@E Map<Object @C [] @D [], @F Object>>> m1;\n    @B @A Map<@A Object @F [] @E [], @D List<@F @A Map<@B @D Object @A @D [] @D [], @A @B @D Object>>> m2;\n    @A @B Map<@B Map<@B Map<@B List<@F ? extends @A String @C [] @D [] @E @F []>, @A List<@B ? super @D String @E @A @F [] @F []>>, @A List<@C ?>>, @D List<@A Map<@E Object @F [] @D [], List<@D Object>> @A []> @B [] @C []> @D [] m3;\n\n    @A Map<@A Object, @B List<@C Object @D [] @E [] @F []>> m12() {\n        return null;\n    }\n\n    @A List<@B Object> @E [] @F [] l1() {\n        return null;\n    }\n\n    @A List<@B Object @C [] @D []> @E [] @F [] l2() {\n        return null;\n    }\n\n    @L List<Object[][]>[] @L [] l3() {\n        return null;\n    }\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/GenericNestedTypeAnnotations.java",
    "content": "package typeAnnotations;\n\npublic class GenericNestedTypeAnnotations {\n    @A V.U<String>.T<Boolean, Integer, Float> t1;\n    V.@B U<String>.T<Boolean, Integer, Float> t2;\n    V.U<String>.@C T<Boolean, Integer, Float> t3;\n    V.U<@D String>.T<Boolean, Integer, Float> t4;\n    V.U<String>.T<@E Boolean, @F Integer, Float> t5;\n    @L V.U<String>.T<@L Boolean, @F Integer, Float> t6;\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/GenericTypeAnnotations.java",
    "content": "package typeAnnotations;\n\nimport java.util.List;\nimport java.util.Map;\n\npublic class GenericTypeAnnotations {\n    @A Map<? extends String, List<Object>> m1;\n    Map<@B ? extends String, List<Object>> m2;\n    Map<? extends @C String, List<Object>> m3;\n    Map<? extends String, @D List<Object>> m4;\n    Map<? extends String, List<@E Object>> m5;\n    @A @B Map<? extends String, List<Object>> m6;\n    Map<@A @B ? extends String, List<Object>> m7;\n    Map<? extends @C @D String, List<Object>> m8;\n    Map<? extends String, @D @E List<Object>> m9;\n    Map<? extends String, List<@E @F Object>> m10;\n    @A Map<@B ? extends @C String, @L @D List<@E Object>> m11;\n    @A Map<@A Object, @B List<@C Object>> m12() { return null; }\n    @A @B List<@C Object> l1() { return null; }\n    @L Map<? extends String, List<Object>> m13;\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/InterfaceSuperTypeAnnotations.java",
    "content": "package typeAnnotations;\n\npublic interface InterfaceSuperTypeAnnotations extends @B Bar, @L @B BarGeneric<@F String, @L @A String @B []> {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/K.java",
    "content": "package typeAnnotations;\n\npublic @interface K {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/L.java",
    "content": "package typeAnnotations;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\n\n@Target({ElementType.TYPE_USE, ElementType.FIELD})\npublic @interface L {\n}\n\n"
  },
  {
    "path": "testData/src/typeAnnotations/MemberDeclarationTypeAnnotations.java",
    "content": "package typeAnnotations;\n\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.util.function.Consumer;\n\npublic class MemberDeclarationTypeAnnotations<@A P extends @B Number & @F Serializable> {\n    @L String s1 = \"\";\n\n    @B int f1 = 0;\n\n    Consumer<String> c = (@A String s) -> System.out.println(s);\n\n    SomeFunInterface<String, String> sf = (String s1, @B String s2) -> System.out.println(s1);\n\n    @K\n    public @L @A MemberDeclarationTypeAnnotations() {\n    }\n\n    @K\n    public <@A T extends @B Number & @F Serializable> @L @C Number foo(@D T @E [] a) {\n        return 0;\n    }\n\n    @K\n    public <T> @C Number bar(@D T @E [] a) throws @A IOException, @B IllegalStateException {\n        return 0;\n    }\n\n    public void fooBar(@L @A String param1, @L @K @B String param2) { }\n}"
  },
  {
    "path": "testData/src/typeAnnotations/NestedTypeAnnotations.java",
    "content": "package typeAnnotations;\n\npublic class NestedTypeAnnotations {\n    @A Z.Y.X.W w1;\n    Z.@B Y.X.W w2;\n    Z.Y.@C X.W w3;\n    Z.Y.X.@D W w4;\n    @A Z.@B Y.@C X.@D W w5;\n    @L Z.Y.X.W w6;\n    P.@A Q.R r1;\n    P. Q. @B R r2;\n    S.T. @C U u1;\n    T. @A Y.U.I.O o1;\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/P.java",
    "content": "package typeAnnotations;\n\npublic class P {\n    public static class Q {\n        public class R {\n\n        }\n    }\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/ParentInterface.java",
    "content": "package typeAnnotations;\n\npublic interface ParentInterface {\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/S.java",
    "content": "package typeAnnotations;\n\npublic class S {\n    public static class T {\n        public static class U {\n\n        }\n    }\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/SomeFunInterface.java",
    "content": "package typeAnnotations;\n\n@FunctionalInterface\npublic interface SomeFunInterface<T, E> {\n    void accept(T t, E e);\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/T.java",
    "content": "package typeAnnotations;\n\npublic class T {\n    public static class Y {\n        public class U {\n            public class I {\n                public class O {\n\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "testData/src/typeAnnotations/V.java",
    "content": "package typeAnnotations;\n\nclass V {\n    class U<J> {\n        class T<K, L, M> {}\n    }\n}"
  },
  {
    "path": "testData/src/typeAnnotations/Z.java",
    "content": "package typeAnnotations;\n\npublic class Z {\n    class Y {\n        class X {\n            class W {\n            }\n        }\n    }\n}\n"
  }
]