diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 44ab3dbb..1917b572 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,13 +1,22 @@ \ No newline at end of file +--> diff --git a/.github/workflows/automatic_release.yml b/.github/workflows/automatic_release.yml new file mode 100644 index 00000000..1c636e02 --- /dev/null +++ b/.github/workflows/automatic_release.yml @@ -0,0 +1,41 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 8 + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'adopt' + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Build with Maven + run: mvn -B package --file pom.xml + - name: Rename final file + run: mv target/deobfuscator-*.jar target/deobfuscator.jar + - name: Prerelease + uses: "marvinpinto/action-automatic-releases@latest" + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "latest" + prerelease: false + title: "Latest Build" + files: | + LICENSE + target/deobfuscator.jar diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..29e3629a --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,27 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: PR integration check + +on: [pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 8 + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'adopt' + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Build with Maven + run: mvn -B package --file pom.xml diff --git a/.gitignore b/.gitignore index 754d3040..e68c0623 100644 --- a/.gitignore +++ b/.gitignore @@ -4,15 +4,18 @@ /src/test/resources/Krakatau /src/test/resources/testcases/*.class /sn/ +/libraries/ +/samples/ /issues/ -/src/main/java/com/javadeobfuscator/deobfuscator/analyzernew/ .classpath .project deobfuscator.iml /.settings /.idea +*.iml *.jar *.txt *.ser *.DS_Store -dependency-reduced-pom.xml \ No newline at end of file +*.yaml +dependency-reduced-pom.xml diff --git a/CUSTOMTRANSFORMER.md b/CUSTOMTRANSFORMER.md new file mode 100644 index 00000000..52a22cd7 --- /dev/null +++ b/CUSTOMTRANSFORMER.md @@ -0,0 +1,33 @@ +# Custom Transformers +So you need to deobfuscate some custom obfuscation? Maybe this repo isn't updated for the latest obfuscators? +Here's some tips for writing a custom transformer. + +### Disassemble your target program +Try using [Krakatau](https://github.com/Storyyeller/Krakatau), it's pretty good. + +### Identify patterns +Most of the time obfuscations are applied with a very simple pattern. For example, string encryption may look like this +```jasmin +ldc "someencryptedstring" +invokestatic foo/DecryptorClass decrypt (Ljava/lang/String;)Ljava/lang/String; +``` +```jasmin +ldc "someencryptedstring" +dup +invokevirtual java/lang/String length ()I +ldc 52 +sipush 29 +ixor +imul +invokestatic foo/DecryptorClass decrypt(Ljava/lang/String;I)Ljava/lang/String; +``` +If you can identify the common pattern being used, you can use [InstructionPattern](https://github.com/java-deobfuscator/deobfuscator/blob/master/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InstructionPattern.java) +to quickly isolate the relevant bytecodes. +For example, at the time of writing Stringer v3 follows some very simple [patterns](https://github.com/java-deobfuscator/deobfuscator/blob/master/src/main/java/com/javadeobfuscator/deobfuscator/transformers/stringer/v3/utils/Constants.java#L60). +### Deobfuscate it +[JavaVM](https://github.com/java-deobfuscator/javavm) provides a very easy way to execute unsafe bytecodes +and intercept/modify results. Again, check out the [Stringer transformers](https://github.com/java-deobfuscator/deobfuscator/blob/master/src/main/java/com/javadeobfuscator/deobfuscator/transformers/stringer/v3/StringEncryptionTransformer.java) for sample usages + +### Open a ticket +If that didn't work, or if you think the repo needs an update, open a ticket and provide the file (or a reproducible sample). +This way, the next person who comes along can use an existing transformer! \ No newline at end of file diff --git a/README.md b/README.md index 0e25aedf..79b5d9a1 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,59 @@ -# Deobfuscator [![Build Status](https://ci.samczsun.com/buildStatus/icon?job=java-deobfuscator/Deobfuscator)](https://ci.samczsun.com/job/java-deobfuscator/job/Deobfuscator) +# Deobfuscator + +This project aims to deobfuscate most commercially-available obfuscators for Java. -This is an all-in-one Java deobfuscator which will deobfuscate code obfuscated by most obfuscators available on the market. +## Updates +To download an updated version of Java Deobfuscator, go to the releases tab. -## What can the deobfuscator do? +If you would like to run this program with a GUI, go to https://github.com/java-deobfuscator/deobfuscator-gui and grab a download. Put the deobfuscator-gui.jar in the same folder as deobfuscator.jar. -The deobfuscator supports deobfuscation of transformations such as string literal encryption, or reflection obfuscation. These transformations have been hard coded for a specific obfuscator, but generic deobfuscators are also available. +## Quick Start -## What can't the deobfuscator do? - -Things like method names, class names, etc cannot be deobfuscated because there renaming is irreversible. The information needed to deobfuscate is removed. - -## Examples - -### As a library - -```java -public class SomeRandomDeobfuscator { - public static void main(String[] args) throws Throwable { - new Deobfuscator() - .withInput(new File("input.jar")) - .withOutput(new File("output.jar")) - .withClasspath(new File("path/to/rt.jar")) - .withTransformer(Transformers.General.SYNTHETIC_BRIDGE) - .start(); - } -} +* [Download](https://github.com/java-deobfuscator/deobfuscator/releases) the deobfuscator. The latest build is recommended. +* If you know what obfuscators were used, skip the next two steps +* Create `detect.yml` with the following contents. Replace `input.jar` with the name of the input +```yaml +input: input.jar +detect: true ``` +* Run `java -jar deobfuscator.jar --config detect.yml` to determine the obfuscators used +* Create `config.yml` with the following contents. Replace `input.jar` with the name of the input +```yaml +input: input.jar +output: output.jar +transformers: + - [fully-qualified-name-of-transformer] + - [fully-qualified-name-of-transformer] + - [fully-qualified-name-of-transformer] + - ... etc +``` +* Run `java -jar deobfuscator.jar` +* Re-run the detection if the JAR was not fully deobfuscated - it's possible to layer obfuscations -### CLI - -If you don't want to import the project, you can always use the command line interface. There are four arguments that are taken. +Take a look at [USAGE.md](USAGE.md) or [wiki](https://github.com/java-deobfuscator/deobfuscator/wiki) for more information. -| Argument | Description | -| --- | --- | -| -input | The JAR to deobfuscate | -| -output | The file to write to | -| -transformer | A canonical name of the transformer class| -| -path | A dependency of the JAR being deobfuscated | +## It didn't work -You may specify multiple transformers, and they will be applied in the order given. Order does matter as sometimes one transformation depends on another not being present. +If you're trying to recover the names of classes or methods, tough luck. That information is typically stripped out and there's no way to recover it. -If you wish to use one of the default transformers, then you may remove the `com.javadeobfuscator.deobfuscator.transformers` prefix. For example, the command below will do the same as the example above. +If you are using one of our transformers, check out the [commonerrors](commonerrors) folder to check for tips. -`java -jar deobfuscator.jar -input input.jar -output output.jar -transformer general.SyntheticBridgeTransformer -path path/to/rt.jar` +Otherwise, check out [this guide](CUSTOMTRANSFORMER.md) on how to implement your own transformer (also, open a issue/PR so I can add support for it) -## Transformers - -Official transformers are linked via the `Transformers` class. - -| Transformer | Canonical Name | Description | -| --- | --- | --- | -| Allatori.STRING_ENCRYPTION | allatori.StringEncryptionTransformer | Decrypts strings encrypted by Allatori | -| DashO.STRING_ENCRYPTION | dasho.StringEncryptionTransformer | Decrypts strings encrypted by DashO | -| Stringer.STRING_ENCRYPTION | stringer.StringEncryptionTransformer | Decrypts strings encrypted by Stringer | -| Stringer.INVOKEDYNAMIC | stringer.InvokedynamicTransformer | Decrypts invokedynamic obfuscated calls by Stringer (Below version 3.0.0) | -| Stringer.REFLECTION_OBFUSCATION | stringer.ReflectionObfuscationTransformer | Decrypts reflection obfuscated calls by Stringer (Below version 3.0.0) | -| Stringer.HIDEACCESS_OBFUSCATION | stringer.HideAccessObfuscationTransformer | Decrypts hide access by Stringer (Included invokedynamic and reflection) | -| Zelix.STRING_ENCRYPTION | zelix.StringEncryptionTransformer | Decrypts strings encrypted by Zelix | -| Zelix.REFLECTION_OBFUSCATION | zelix.ReflectionObfuscationTransformer | Decrypts reflection obfuscated calls by Zelix | -| General.PEEPHOLE_OPTIMIZER | general.peephole.PeepholeOptimizer| Optimizes the code | -| General.Removers.SYNTHETIC_BRIDGE | general.remover.SyntheticBridgeRemover | Removes synthetic and bridge modifiers from all methods and fields | -| General.Removers.LINE_NUMBER | general.remover.LineNumberRemover | Removes line number metadata | -| General.Removers.ILLEGAL_VARARGS | general.remover.IllegalVarargsRemover | Unmangles methods marked as variadic but aren't really | -| General.Removers.ILLEGAL_SIGNATURE | general.remover.IllegalSignatureRemover | Removes illegal signatures from members | -| Normalizer.CLASS_NORMALIZER | normalizer.ClassNormalizer | Renames all classes to Class | -| Normalizer.METHOD_NORMALIZER | normalizer.MethodNormalizer | Renames all methods to Method | -| Normalizer.FIELD_NORMALIZER | normalizer.FieldNormalizer | Renames all fields to Field | -| Normalizer.PACKAGE_NORMALIZER | normalizer.PackageNormalizer | Renames all packages to Package | -| Normalizer.SOURCEFILE_CLASS_NORMALIZER | normalizer.SourceFileClassNormalizer | Recovers `SourceFile` attributes when possible | -| Normalizer.VARIABLE_NORMALIZER | normalizer.VariableNormalizer | Renames all local variables to var | - -## Downloads +## Supported Obfuscators -The latest build can be downloaded from my [CI Server](https://ci.samczsun.com/job/java-deobfuscator/job/Deobfuscator/) +* [Zelix Klassmaster](http://www.zelix.com/) +* [Stringer](https://jfxstore.com/stringer/) +* [Allatori](http://www.allatori.com/) +* [DashO](https://www.preemptive.com/products/dasho/overview) +* [DexGuard](https://www.guardsquare.com/dexguard) +* [ClassGuard](https://www.zenofx.com/classguard/) +* Smoke (dead, website archive [here](https://web.archive.org/web/20170918112921/https://newtownia.net/smoke/)) +* SkidSuite2 (dead, archive [here](https://github.com/GenericException/SkidSuite/tree/master/archive/skidsuite-2)) -## Supported Obfuscators +## List of Transformers -[Zelix Klassmaster](http://www.zelix.com/) -[Stringer](https://jfxstore.com/stringer/) -[Allatori](http://www.allatori.com/) -[DashO](https://www.preemptive.com/products/dasho/overview) -[DexGuard](https://www.guardsquare.com/dexguard) -[Smoke](https://newtownia.net/smoke) -Generic obfuscation +The automagic detection should be able to recommend the transformers you'll need to use. However, it may not be up to date. If you're familiar with Java reverse engineering, feel free to [take a look around](src/main/java/com/javadeobfuscator/deobfuscator/transformers) and use what you need. ## FAQs @@ -94,8 +65,10 @@ You need to specify all the JARs that the input file references. You'll almost a Increase your stack size. For example, `java -Xss128m -jar deobfuscator.jar` #### Does this work on Android apps? -Technically, yes, you could use something like [dex2jar](https://github.com/pxb1988/dex2jar) or [enjarify](https://github.com/storyyeller/enjarify), but try [simplify](https://github.com/CalebFenton/simplify) first. -It's a deobfuscator of sorts built specifically for Android. +Technically, yes, you could use something like [dex2jar](https://github.com/pxb1988/dex2jar) +or [enjarify](https://github.com/storyyeller/enjarify). However, dex -> jar conversion is lossy at best. +Try [simplify](https://github.com/CalebFenton/simplify) or [dex-oracle](https://github.com/CalebFenton/dex-oracle) first. +They were written specifically for Android apps. ## Licensing diff --git a/USAGE.md b/USAGE.md new file mode 100644 index 00000000..c6131ca9 --- /dev/null +++ b/USAGE.md @@ -0,0 +1,47 @@ +# Usage +### As a library + +```java +public class SomeRandomDeobfuscator { + public static void main(String[] args) throws Throwable { + Configuration config = new Configuration(); + config.setInput(new File("input.jar")); + config.setOutput(new File("output.jar")); + config.setPath(Arrays.asList( + new File("C:\\Program Files\\Java\\jdk_8\\jre\\lib\\rt.jar"), + new File("C:\\Program Files\\Java\\jdk_8\\jre\\lib\\jce.jar"), + new File("C:\\Program Files\\Java\\jdk_8\\jre\\lib\\ext\\jfxrt.jar"), + new File("C:\\Program Files\\Java\\jdk_8\\lib\\tools.jar") + )); + config.setTransformers(Arrays.asList( + TransformerConfig.configFor(PeepholeOptimizer.class) + )); + new Deobfuscator(config).start(); + } +} +``` + +### CLI + +If you don't want to import the project, you can always use the command line interface. + +| Argument | Description | +| --- | --- | +| --config | The configuration file | + +You may specify multiple transformers, and they will be applied in the order given. Order does matter as sometimes one transformation depends on another not being present. + +If you wish to use one of the default transformers, then you may remove the `com.javadeobfuscator.deobfuscator.transformers` prefix. + +Here is a sample `config.yaml`: + +```yaml +input: input.jar +output: output.jar +transformers: + - normalizer.MethodNormalizer: + mapping-file: normalizer.txt + - stringer.StringEncryptionTransformer + - normalizer.ClassNormalizer: {} + normalizer.FieldNormalizer: {} +``` diff --git a/commonerrors/Allatori.md b/commonerrors/Allatori.md new file mode 100644 index 00000000..793438ad --- /dev/null +++ b/commonerrors/Allatori.md @@ -0,0 +1,5 @@ +## Flow Obfuscation +The latest versions of Allatori contain some flow obfuscation which moves around some instructions, so it is necessary to run allatori.FlowObfuscationTransformer to fix this. This transformer should always work, but if it doesn't, open an issue (an exception is if there is another type of flow obfuscation layered over it)! Note: It is not necessary to run this before allatori.StringEncryptionTransformer. + +## Transformers to Use +allatori.StringEncryptionTransformer works the fastest, although in rare cases where it doesn't work, you can use allatori.string.StringEncryptionTransformer. \ No newline at end of file diff --git a/commonerrors/AntiReleak.md b/commonerrors/AntiReleak.md new file mode 100644 index 00000000..eaa9c3ad --- /dev/null +++ b/commonerrors/AntiReleak.md @@ -0,0 +1,2 @@ +## InvokeDynamic Obfuscation +This transformer was made to be a universal decryptor for all antireleak invokedynamics. This will only reverse the invokedynamics so you can reverse engineer the plugin, not remove all traces of your user id! \ No newline at end of file diff --git a/commonerrors/DashO.md b/commonerrors/DashO.md new file mode 100644 index 00000000..cf5b6899 --- /dev/null +++ b/commonerrors/DashO.md @@ -0,0 +1,5 @@ +## String Encryption +If you get an error relating to SourceResult or if not all strings are getting decrypted, it is because DashO's flow obfuscation was applied, which messes with the string decryptor. Run dasho.FakeExceptionTransformer and/or dasho.FlowObfuscationTransformer first, and you will able to decrypt all the strings. + +## Transformers to Use +dasho.StringEncryptionTransformer works the fastest (and it cleans up properly), although in rare cases where it doesn't work (you get an error not related to SourceResult), you can use dasho.string.StringEncryptionTransformer. diff --git a/commonerrors/General.md b/commonerrors/General.md new file mode 100644 index 00000000..c096546c --- /dev/null +++ b/commonerrors/General.md @@ -0,0 +1,17 @@ +## Ordering +In general, files must be deobfuscated in the reverse order in which obfuscators were applied. For example, if your file is obfuscated with Stringer and then Allatori, you would run the Allatori transformers, and then the Stringer ones. Also, if an obfuscator was applied multiple times (e.g. several layers of Allatori), you must run the transformers multiple times. The only exception with this rule is with flow obfuscation transformers, which typically do not need to be run multiple times. + +## Write Errors +If you see an error similar to "ClassA could not be found while writing ClassB" it is NOT a deobfuscation error. It just means that you have not added the right libraries. Unless you are trying to run the outputted file (Note: There are no guarantees that it will run, especially with obfuscators like ZKM) it will not affect you. + +## Adding Libraries +Adding libraries is usually good practice, but isn't necessary unless you are dealing with reflection obfuscation. If you are having trouble adding libraries, you can use deobfuscator-gui (https://github.com/java-deobfuscator/deobfuscator-gui) and add the library JARs to the "Path" tab. Libraries are what the program needs to run, so rt.jar (look this up!) should always be added. Then, check what JARs need to be available (for example, spigot plugins would need the spigot JAR) and add them accordingly. + +## ZIP Compression Errors +If your file cannot be opened in Bytecode Viewer or another decompiler (and it has valid classes), it may be using a certain ZIP compression that crashes decompilers. In that case, be sure to run deobfuscator on it once with no transformers, and then decompile the output ZIP. This will most likely fix the error. + +## ASM Errors +If the class files cannot be loaded (an error like IllegalArgumentException appears) and you are **certain** that they are valid, that means that an ASM exploit is being used. Since ASM is unable to parse these files, the deobfuscator is unable to deobfuscate it. + +## Normalizers +Normalizers should always be used last. If it is used first, then any string or reference obfuscation transformers applied afterwards will most likely fail, because they require that the classes and methods to retain their obfuscated names. \ No newline at end of file diff --git a/commonerrors/Radon.md b/commonerrors/Radon.md new file mode 100644 index 00000000..1bc720e8 --- /dev/null +++ b/commonerrors/Radon.md @@ -0,0 +1,2 @@ +## RadonTransformer(V2) +RadonTransformer was meant to work on the earlier versions of Radon, while RadonTransformerV2 works on the later versions. Use RadonTransformer first, and if it doesn't work, use RadonTransformerV2. If the file was partially deobfuscated after running the transformer once, run it again. Also, if your file has invokedynamic obfuscation, you should add all required libraries. \ No newline at end of file diff --git a/commonerrors/Stringer.md b/commonerrors/Stringer.md new file mode 100644 index 00000000..afa27298 --- /dev/null +++ b/commonerrors/Stringer.md @@ -0,0 +1,5 @@ +## HideAccess Obfuscation +If you see an error while running the transformer, it means that you did not remove the string encryption. Run stringer.StringEncryptionTransformer first and you will be able decrypt Stringer's hide access. Note that you need to add all libraries that are used by the JAR, otherwise you will not be able to decrypt the hide access! + +## Other Stringer Transformers +In general, you should only use stringer.HideAccessTransformer, stringer.StringEncryptionTransformer, and stringer.ResourceEncryptionTransformer. The stringer.vX transformers are meant for specific versions of Stringer and should not be combined with the stringer.StringEncryption or stringer.HideAccess transformers. diff --git a/commonerrors/Superblaubeere.md b/commonerrors/Superblaubeere.md new file mode 100644 index 00000000..58bc08e6 --- /dev/null +++ b/commonerrors/Superblaubeere.md @@ -0,0 +1,2 @@ +## Superblaubeere27's Obfuscator +One issue with this obfuscator is that it may inject local variables into the code, which prevents the file from being completely deobfuscated. In that case, use general.LocalVariableRemover, write to file, and then run the deobfuscator on the output. If you are still having issues with some features not being deobfuscated, use DeadCodeRemover and then RedundantGotoRemover to get rid of the junk code that may be in the way. \ No newline at end of file diff --git a/commonerrors/UnknownObf.md b/commonerrors/UnknownObf.md new file mode 100644 index 00000000..9efc3871 --- /dev/null +++ b/commonerrors/UnknownObf.md @@ -0,0 +1,5 @@ +## Finding Obfuscators +If the "detect" option did not show any obfuscators, be sure to try running most of the transformers (excluding the general category) first. If none of them are able to deobfuscate the file, check if the file doesn't just have name obfuscation (which is not reversible, but you can rename them to generic names using normalizers). It is recommended to decompile the file to try to find the type of obfuscation, as the detect feature is not perfect. Scroll to the bottom of this repo to get some examples of code to look for: https://github.com/GraxCode/threadtear + +## A Note on the Detect Feature +The detect feature mainly searches for string encryption and sometimes reflection obfuscation methods. This means that if a file is only obfuscated with flow obfuscation, the detect feature will not be able to find the type of obfuscation used. Also, obfuscators that are rarely seen (like SkidSuite) or have too many variations (like Radon) will not be detected. \ No newline at end of file diff --git a/commonerrors/Zelix.md b/commonerrors/Zelix.md new file mode 100644 index 00000000..bfd5e7ed --- /dev/null +++ b/commonerrors/Zelix.md @@ -0,0 +1,11 @@ +## String Encryption +The string encryption transformer (zelix.StringEncryptionTransformer) supports ZKM 5-8. If the file was obfuscated using ZKM 9 and up, the strings will only be decrypted if the file lacks method parameter changes. + +## Reference/Reflection Obfuscation +The reference obfuscation transformer supports both the non-invokedynamic and invokedynamic variants of the obfuscation, but only if method parameter changes aren't enabled. Note that you must use the Zelix string encryption transformer first, or the deobfuscation will fail. Also note that you must add the libraries to path or the deobfuscation will also fail! + +## Flow Obfuscation +The flow obfuscation transformer should generally be applied after the string encryption transformer, but if a file was obfuscated with multiple layers of ZKM, you should instead use FlowObfuscationTransformer first. Also, the flow deobfuscation will be limited if the code was obfuscated with method parameter changes. + +## Transformers to Use +You should stick to using zelix.StringEncryptionTransformer as it is the fastest and decrypts the most cases. It is not recommended to combine the zelix.string transformers with zelix.StringEncryptionTransformer. \ No newline at end of file diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 00000000..1451379a --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +javadeobfuscator.com \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..c4192631 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..8c56aee3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,75 @@ +# Deobfuscator + +This project aims to deobfuscate most commercially-available obfuscators for Java. + +## Updates +To download an updated version of Java Deobfuscator, go to the releases tab. + +If you would like to run this program with a GUI, go to https://github.com/java-deobfuscator/deobfuscator-gui and grab a download. Put the deobfuscator-gui.jar in the same folder as deobfuscator.jar. + +## Quick Start + +* [Download](https://github.com/java-deobfuscator/deobfuscator/releases) the deobfuscator. +* If you know what obfuscators were used, skip the next two steps +* Create `detect.yml` with the following contents. Replace `input.jar` with the name of the input +```yaml +input: input.jar +detect: true +``` +* Run `java -jar deobfuscator.jar --config detect.yml` to determine the obfuscators used +* Create `config.yml` with the following contents. Replace `input.jar` with the name of the input +```yaml +input: input.jar +output: output.jar +transformers: + - [fully-qualified-name-of-transformer] + - [fully-qualified-name-of-transformer] + - [fully-qualified-name-of-transformer] + - ... etc +``` +* Run `java -jar deobfuscator.jar` +* Re-run the detection if the JAR was not fully deobfuscated - it's possible to layer obfuscations + +Take a look at [USAGE.md](USAGE.md) for more information. + +## It didn't work + +If you're trying to recover the names of classes or methods, tough luck. That information is typically stripped out and there's no way to recover it. + +If you are using one of our transformers, check out the commonerrors folder to check for tips. + +Otherwise, check out [this guide](CUSTOMTRANSFORMER.md) on how to implement your own transformer (also, open a issue/PR so I can add support for it) + +## Supported Obfuscators + +[Zelix Klassmaster](http://www.zelix.com/) +[Stringer](https://jfxstore.com/stringer/) +[Allatori](http://www.allatori.com/) +[DashO](https://www.preemptive.com/products/dasho/overview) +[DexGuard](https://www.guardsquare.com/dexguard) +[ClassGuard](https://www.zenofx.com/classguard/) +[Smoke](https://newtownia.net/smoke) +SkidSuite2 (dead, some forks are listed [here](https://github.com/tetratec/SkidSuite2/network/members)) + +## List of Transformers + +The automagic detection should be able to recommend the transformers you'll need to use. However, it may not be up to date. If you're familiar with Java reverse engineering, feel free to [take a look around](https://github.com/java-deobfuscator/deobfuscator/tree/master/src/main/java/com/javadeobfuscator/deobfuscator/transformers) and use what you need. + +## FAQs + +#### I got an error that says "Could not locate a class file" +You need to specify all the JARs that the input file references. You'll almost always need to add `rt.jar` +(which contains all the classes used by the Java Runtime) + +#### I got an error that says "A StackOverflowError occurred during deobfuscation" +Increase your stack size. For example, `java -Xss128m -jar deobfuscator.jar` + +#### Does this work on Android apps? +Technically, yes, you could use something like [dex2jar](https://github.com/pxb1988/dex2jar) +or [enjarify](https://github.com/storyyeller/enjarify). However, dex -> jar conversion is lossy at best. +Try [simplify](https://github.com/CalebFenton/simplify) or [dex-oracle](https://github.com/CalebFenton/dex-oracle) first. +They were written specifically for Android apps. + +## Licensing + +Java Deobfuscator is licensed under the Apache 2.0 license. \ No newline at end of file diff --git a/pom.xml b/pom.xml index ada75fd2..f4ab5889 100644 --- a/pom.xml +++ b/pom.xml @@ -6,6 +6,8 @@ 1.0.0 UTF-8 + 9.5 + 1.10.2 src/main/java @@ -27,7 +29,7 @@ maven-compiler-plugin - 3.3 + 3.10.1 1.8 1.8 @@ -40,7 +42,7 @@ org.apache.maven.plugins maven-shade-plugin - 2.4.2 + 3.3.0 package @@ -48,12 +50,18 @@ shade + true *:* - true + + + @@ -61,7 +69,7 @@ org.apache.maven.plugins maven-jar-plugin - 2.4 + 3.2.2 @@ -72,17 +80,32 @@ + + + java-deobfuscator-repository + https://repo.samczsun.com/repository/java-deobfuscator + + + jitpack.io + https://jitpack.io + + commons-cli commons-cli - 1.3.1 + 1.5.0 + + + commons-io + commons-io + 2.11.0 com.google.guava guava - 19.0 + 31.1-jre org.jooq @@ -92,19 +115,89 @@ com.google.code.gson gson - 2.6.2 + 2.9.0 + + + com.fasterxml.jackson.core + jackson-core + 2.13.4 + + + com.fasterxml.jackson.core + jackson-databind + 2.13.4.1 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.13.4 + + + + org.yaml + snakeyaml + 1.32 + + + org.slf4j + slf4j-api + 1.8.0-beta4 + + + org.slf4j + slf4j-simple + 1.8.0-beta4 junit junit - 4.12 + 4.13.2 test - commons-io - commons-io - 2.5 - test + javax.xml.bind + jaxb-api + 2.3.1 + + + + + org.ow2.asm + asm + ${version.asm} + + + org.ow2.asm + asm-commons + ${version.asm} + + + org.ow2.asm + asm-util + ${version.asm} + + + org.ow2.asm + asm-tree + ${version.asm} + + + org.ow2.asm + asm-analysis + ${version.asm} + + + + + com.javadeobfuscator + javavm + 3.0.0 + + + + com.github.Col-E + CAFED00D + ${version.dude} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java b/src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java index e64059ab..41356500 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java @@ -16,182 +16,443 @@ package com.javadeobfuscator.deobfuscator; -import com.javadeobfuscator.deobfuscator.exceptions.NoClassInPathException; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.ClassReader; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.ClassWriter; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.commons.JSRInlinerAdapter; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.AbstractInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.ClassNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.util.CheckClassAdapter; -import com.javadeobfuscator.deobfuscator.transformers.Transformer; -import com.javadeobfuscator.deobfuscator.utils.ClassTree; -import com.javadeobfuscator.deobfuscator.utils.Utils; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; - import java.io.File; import java.io.FileOutputStream; -import java.io.InputStream; +import java.io.IOException; import java.lang.reflect.Modifier; import java.util.AbstractMap.SimpleEntry; import java.util.*; import java.util.Map.Entry; -import java.util.stream.Collectors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -public class Deobfuscator { +import com.javadeobfuscator.deobfuscator.asm.ConstantPool; +import com.javadeobfuscator.deobfuscator.config.Configuration; +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.exceptions.NoClassInPathException; +import com.javadeobfuscator.deobfuscator.rules.Rule; +import com.javadeobfuscator.deobfuscator.rules.Rules; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import com.javadeobfuscator.deobfuscator.utils.ClassTree; +import com.javadeobfuscator.deobfuscator.utils.Utils; +import me.coley.cafedude.classfile.ClassFile; +import me.coley.cafedude.InvalidClassException; +import me.coley.cafedude.io.ClassFileReader; +import me.coley.cafedude.io.ClassFileWriter; +import me.coley.cafedude.transform.IllegalStrippingTransformer; +import org.apache.commons.io.IOUtils; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.JSRInlinerAdapter; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.util.CheckClassAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; - private List> transformers = new ArrayList<>(); - private List classpathFiles = new ArrayList<>(); - private Map classpath = new HashMap<>(); - private Map classes = new HashMap<>(); - private Map hierachy = new HashMap<>(); - private File input; - private File output; +public class Deobfuscator { private static final boolean DEBUG = false; + + private final Logger logger = LoggerFactory.getLogger(Deobfuscator.class); + + private final Configuration configuration; + + private final Set missingRefs = new HashSet<>(); + private final Map classpath = new HashMap<>(); + private final Map libraries = new HashMap<>(); + private final Map classes = new HashMap<>(); + private final Map hierachy = new HashMap<>(); + private final Set libraryClassnodes = new HashSet<>(); /** - * Some obfuscators like to have junk classes. If ALL your libraries are added, - * enable this to dump troublesome classes. Note that this will not get rid of all junk classes. + * Entries from the input jar that will be passed through to the output */ - private static final boolean DELETE_USELESS_CLASSES = false; + private final Map inputPassthrough = new HashMap<>(); + /** + * Constant pool data since ClassNodes don't support custom data + */ + private final Map constantPools = new HashMap<>(); + private final Map readers = new HashMap<>(); + public Map invalidClasses = new HashMap<>(); + public List junkFiles = new ArrayList<>(); + + public Deobfuscator(Configuration configuration) { + this.configuration = configuration; + } + + public ConstantPool getConstantPool(ClassNode classNode) { + return this.constantPools.get(classNode); + } + + public void setConstantPool(ClassNode owner, ConstantPool pool) { + this.constantPools.put(owner, pool); + } + + public Map getConstantPools() { + return this.constantPools; + } - public Deobfuscator withTransformer(Class transformer) { - this.transformers.add(transformer); - return this; + public Map getInputPassthrough() { + return inputPassthrough; } - public Deobfuscator withClasspath(File file) { - if (file.isDirectory()) { - File[] files = file.listFiles((dir, name) -> name.endsWith(".jar")); - if (files != null) { - classpathFiles.addAll(Arrays.asList(files)); + public Configuration getConfig() { + return this.configuration; + } + + public Map getClasses() { + return this.classes; + } + + public Map getReaders() { + return readers; + } + + public Map getLibraries() { + return libraries; + } + + private Map loadClasspathFile(File file, boolean skipCode) throws IOException { + Map map = new HashMap<>(); + + ZipFile zipIn = new ZipFile(file); + Enumeration entries = zipIn.entries(); + while (entries.hasMoreElements()) { + ZipEntry ent = entries.nextElement(); + if (ent.getName().endsWith(".class")) { + try { + ClassReader reader = new ClassReader(zipIn.getInputStream(ent)); + ClassNode node = new ClassNode(); + reader.accept(node, (skipCode ? 0 : 0) | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + map.put(node.name, node); + + setConstantPool(node, new ConstantPool(reader)); + } catch (Exception ex) { + logger.warn("Could not load class " + ent.getName() + " from library " + file, ex); + } } - } else { - this.classpathFiles.add(file); } - return this; + zipIn.close(); + + return map; } - public Deobfuscator withInput(File input) { - this.input = input; - return this; + private void loadClasspath() throws IOException { + if (configuration.getPath() != null) { + for (File file : configuration.getPath()) { + if (file.isFile()) { + classpath.putAll(loadClasspathFile(file, true)); + } else { + File[] files = file.listFiles(child -> child.getName().endsWith(".jar")); + if (files != null) { + for (File child : files) { + classpath.putAll(loadClasspathFile(child, true)); + } + } + } + } + } + if (configuration.getLibraries() != null) { + for (File file : configuration.getLibraries()) { + if (file.isFile()) { + libraries.putAll(loadClasspathFile(file, false)); + } else { + File[] files = file.listFiles(child -> child.getName().endsWith(".jar")); + if (files != null) { + for (File child : files) { + libraries.putAll(loadClasspathFile(child, false)); + } + } + } + } + } + classpath.putAll(libraries); + libraryClassnodes.addAll(classpath.values()); } - public Deobfuscator withOutput(File output) { - this.output = output; - return this; + private boolean isClassIgnored(ClassNode classNode) { + if (configuration.getIgnoredClasses() == null) { + return false; + } + + for (String ignored : configuration.getIgnoredClasses()) { + Pattern pattern; + try { + pattern = Pattern.compile(ignored); + } catch (PatternSyntaxException e) { + logger.error("Error while compiling pattern for ignore statement {}", ignored, e); + continue; + } + Matcher matcher = pattern.matcher(classNode.name); + if (matcher.find()) { + return true; + } + } + return false; } - public void start() throws Throwable { - for (File file : classpathFiles) { - ZipFile zipIn = new ZipFile(file); - Enumeration entries = zipIn.entries(); - while (entries.hasMoreElements()) { - ZipEntry ent = entries.nextElement(); - if (ent.getName().endsWith(".class")) { - if (ent.getName().equals("module-info.class")) { // *rolls eyes* + private void loadInput() throws IOException { + if (this.configuration.isParamorphismV2()) { + //Load folder "classes" + try (ZipFile zipIn = new ZipFile(configuration.getInput())) { + Enumeration e = zipIn.entries(); + while (e.hasMoreElements()) { + ZipEntry next = e.nextElement(); + if (next.isDirectory() && next.getName().endsWith(".class/")) { + byte[] data = IOUtils.toByteArray(zipIn.getInputStream(next)); + loadInput(next.getName().substring(0, next.getName().length() - 1), data); + } else if (!next.isDirectory() && next.getName().contains(".class/")) { + junkFiles.add(next.getName()); + } + } + } + } else if (this.configuration.isParamorphism()) { + Map classNameToName = new HashMap<>(); + Map entries = new HashMap<>(); + //Check all duplicate files + try (ZipFile zipIn = new ZipFile(configuration.getInput())) { + Enumeration e = zipIn.entries(); + while (e.hasMoreElements()) { + ZipEntry next = e.nextElement(); + if (next.isDirectory() || !next.getName().endsWith(".class")) { + continue; + } + + try { + byte[] data = IOUtils.toByteArray(zipIn.getInputStream(next)); + ClassReader reader = new ClassReader(data); + ClassNode node = new ClassNode(); + reader.accept(node, ClassReader.SKIP_FRAMES); + if (entries.containsKey(node.name)) { + invalidClasses.put(next.getName(), data); + invalidClasses.put(classNameToName.get(node.name), entries.get(node.name)); + } else { + classNameToName.put(node.name, next.getName()); + entries.put(node.name, data); + } + } catch (Exception ex) { continue; } - ClassReader reader = new ClassReader(zipIn.getInputStream(ent)); - ClassNode node = new ClassNode(); - node.isLibrary = true; - reader.accept(node, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); - WrappedClassNode wrappedClassNode = new WrappedClassNode(node, reader.getItemCount()); - classpath.put(node.name, wrappedClassNode); } } - zipIn.close(); - } - ZipFile zipIn = new ZipFile(input); - ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(output)); - Enumeration e = zipIn.entries(); - while (e.hasMoreElements()) { - ZipEntry next = e.nextElement(); - if (next.getName().endsWith(".class")) { - try { - InputStream in = zipIn.getInputStream(next); - ClassReader reader = new ClassReader(in); - ClassNode node = new ClassNode(); - reader.accept(node, ClassReader.SKIP_FRAMES); //TODO: Skip classpath mode + //Filter out real classes + List real = new ArrayList<>(); + for (Entry entry : invalidClasses.entrySet()) { + ClassReader reader = new ClassReader(entry.getValue()); + ClassNode node = new ClassNode(); + reader.accept(node, ClassReader.SKIP_FRAMES); + if ((node.name + ".class").equals(entry.getKey())) { + real.add(entry.getKey()); + } + } + real.forEach(s -> invalidClasses.remove(s)); + } + try (ZipFile zipIn = new ZipFile(configuration.getInput())) { + Enumeration e = zipIn.entries(); + while (e.hasMoreElements()) { + ZipEntry next = e.nextElement(); + + // Some obfuscators can screw with the ZIP format and store data + // in directory entries, and yes, the JVM is cool with that. + if (next.isDirectory() || next.getName().endsWith(".class/")) { + continue; + } + + byte[] data = IOUtils.toByteArray(zipIn.getInputStream(next)); + loadInput(next.getName(), data); + } + } + } + + public void loadInput(String name, byte[] data) { + boolean passthrough = true; + + if (name.endsWith(".class") || name.endsWith(".class/")) { + // These 'classes' are likely red-herrings using the '.class/' trick. + // So we will toss them since they're not real classes. + if (data.length <= 30) { + return; + } + + try { + // Patching optional, disabling useful for ensuring that output problems related to patch process. + if (configuration.isPatchAsm()) { + ClassFileReader cfr = new ClassFileReader(); + cfr.setDropForwardVersioned(true); + cfr.setDropEofAttributes(true); + ClassFile cf = cfr.read(data); + new IllegalStrippingTransformer(cf).transform(); + ClassFileWriter cfw = new ClassFileWriter(); + data = cfw.write(cf); + } + // Should be compliant now unless a new crash is discovered. + // Check for updates or open an issue on the CAFED00D project if this occurs + ClassReader reader = new ClassReader(data); + ClassNode node = new ClassNode(); + reader.accept(node, ClassReader.SKIP_FRAMES); + readers.put(node, reader); + setConstantPool(node, new ConstantPool(reader)); + + if (!isClassIgnored(node)) { for (int i = 0; i < node.methods.size(); i++) { MethodNode methodNode = node.methods.get(i); - JSRInlinerAdapter adapter = new JSRInlinerAdapter(methodNode, methodNode.access, methodNode.name, methodNode.desc, methodNode.signature, methodNode.exceptions.toArray(new String[0])); + JSRInlinerAdapter adapter = new JSRInlinerAdapter( + methodNode, + methodNode.access, + methodNode.name, + methodNode.desc, + methodNode.signature, + methodNode.exceptions.toArray(new String[0])); methodNode.accept(adapter); node.methods.set(i, adapter); } - WrappedClassNode wr = new WrappedClassNode(node, reader.getItemCount()); - classes.put(node.name, wr); - } catch (IllegalArgumentException x) { - System.out.println("Could not parse " + next.getName() + " (is it a class?)"); - x.printStackTrace(System.out); - zipOut.putNextEntry(new ZipEntry(next.getName())); - Utils.copy(zipIn.getInputStream(next), zipOut); - zipOut.closeEntry(); + + if (!invalidClasses.containsKey(name)) { + classes.put(node.name, node); + } + classpath.put(node.name, node); + passthrough = false; + } else { + classpath.put(node.name, node); + } + } catch (IllegalArgumentException | IndexOutOfBoundsException | InvalidClassException x) { + if (configuration.isParamorphismV2()) { + invalidClasses.put(name, data); + } else if (!configuration.isPatchAsm()) { + logger.error("Could not parse {} (Try adding \"patchAsm: true\" to the config?)", name, x); + } else { + logger.error("Could not parse {} (Is it a class file?)", name, x); } - } else if (!next.isDirectory()) { - zipOut.putNextEntry(new ZipEntry(next.getName())); - Utils.copy(zipIn.getInputStream(next), zipOut); - zipOut.closeEntry(); } } - classpath.putAll(classes); + if (passthrough && !junkFiles.contains(name)) { + inputPassthrough.put(name, data); + } + } - Map>> callers = new HashMap<>(); - classes.values().forEach(wrappedClassNode -> { - wrappedClassNode.classNode.methods.forEach(methodNode -> { + /** + * @deprecated do we need this? + */ + @Deprecated + private void computeCallers() { + Map>> callers = new HashMap<>(); + classes.values().forEach(classNode -> { + classNode.methods.forEach(methodNode -> { for (int i = 0; i < methodNode.instructions.size(); i++) { AbstractInsnNode node = methodNode.instructions.get(i); if (node instanceof MethodInsnNode) { MethodInsnNode mn = (MethodInsnNode) node; - WrappedClassNode targetNode = classes.get(mn.owner); + ClassNode targetNode = classes.get(mn.owner); if (targetNode != null) { - MethodNode targetMethod = targetNode.classNode.methods.stream().filter(m -> m.name.equals(mn.name) && m.desc.equals(mn.desc)).findFirst().orElse(null); - if (targetMethod != null) { - List> caller = callers.get(targetMethod); - if (caller == null) { - caller = new ArrayList<>(); - callers.put(targetMethod, caller); - } - caller.add(new SimpleEntry<>(wrappedClassNode, methodNode)); - } + targetNode.methods.stream() + .filter(m -> m.name.equals(mn.name) && m.desc.equals(mn.desc)) + .findFirst() + .ifPresent(targetMethod -> { + callers.computeIfAbsent(targetMethod, k -> new ArrayList<>()) + .add(new SimpleEntry<>(classNode, methodNode)); + }); } } } }); }); + } - System.out.println(); - System.out.println("Reading complete. Loading hierachy"); - System.out.println(); + public boolean isLibrary(ClassNode classNode) { + return libraryClassnodes.contains(classNode); + } + + public void start() throws Throwable { + logger.info("Loading classpath"); + loadClasspath(); + + logger.info("Loading input"); + loadInput(); + + if (getConfig().isDetect()) { + logger.info("Detecting known obfuscators"); + + for (Rule rule : Rules.RULES) { + try + { + String message = rule.test(this); + if (message == null) { + continue; + } + + logger.info(""); + logger.info("{}: {}", rule.getClass().getSimpleName(), rule.getDescription()); + logger.info("\t{}", message); + logger.info("Recommend transformers:"); + logger.info("(Choose one transformer. If there are multiple, it's recommended to try the transformer listed first)"); + + Collection>> recommended = rule.getRecommendTransformers(); + if (recommended == null) { + logger.info("\tNone"); + } else { + for (Class> transformer : recommended) { + logger.info("\t{}", transformer.getName()); + } + } + }catch(Exception e) + { + e.printStackTrace(); + } + } - loadHierachy(); + logger.info("All detectors have been run. If you do not see anything listed, check if your file only contains name obfuscation."); + logger.info("Do note that some obfuscators do not have detectors."); + return; + } - System.out.println(); - System.out.println("Transforming"); - System.out.println(); + logger.info("Computing callers"); + computeCallers(); + if (configuration.isDeleteUselessClasses()) { + logger.warn("Warning: You have enabled the option \"delete useless classes\"."); + logger.warn("This option will delete any classes whose superclasses or interfaces cannot be resolved for certain transformers."); + logger.warn("This feature is only to be used when your file contains trash classes that prevent transformers from working."); + logger.warn("All libraries must be added for this to work properly."); + } - for (Class transformerClass : transformers) { - Transformer transformer = transformerClass.getConstructor(Map.class, Map.class).newInstance(classes, classpath); - transformer.setDeobfuscator(this); - transformer.transform(); + if (configuration.isSmartRedo()) { + logger.warn("You have enabled \"smart redo\". For some transformers, this may result in an infinite loop."); } - System.out.println(); - System.out.println("Transforming complete. Writing to file"); - System.out.println(); + logger.info("Transforming"); + if (configuration.getTransformers() != null) { + for (TransformerConfig config : configuration.getTransformers()) { + logger.info("Running {}", config.getImplementation().getCanonicalName()); + runFromConfig(config); + } + } + logger.info("Writing"); if (DEBUG) { - classes.values().stream().map(wrappedClassNode -> wrappedClassNode.classNode).forEach(Utils::printClass); + classes.values().forEach(Utils::printClass); } - classes.values().stream().map(wrappedClassNode -> wrappedClassNode.classNode).forEach(classNode -> { + ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(configuration.getOutput())); + inputPassthrough.forEach((name, val) -> { + ZipEntry entry = new ZipEntry(name); + try { + zipOut.putNextEntry(entry); + zipOut.write(val); + zipOut.closeEntry(); + } catch (IOException e) { + logger.error("Error writing entry {}", name, e); + } + }); + + classes.values().forEach(classNode -> { try { byte[] b = toByteArray(classNode); if (b != null) { @@ -199,37 +460,69 @@ public void start() throws Throwable { zipOut.write(b); zipOut.closeEntry(); } - } catch (Throwable t) { - System.out.println("Uncaught error"); - t.printStackTrace(System.out); + } catch (IOException e) { + logger.error("Error writing entry {}", classNode.name, e); } }); + zipOut.close(); - zipIn.close(); + } + + public boolean runFromConfig(TransformerConfig config) throws Throwable { + Transformer transformer = config.getImplementation().newInstance(); + transformer.init(this, config, classes, classpath, readers); + boolean madeChangesAtLeastOnce = false; + boolean madeChanges; + do { + madeChanges = transformer.transform(); + madeChangesAtLeastOnce = madeChangesAtLeastOnce || madeChanges; + } while (madeChanges && getConfig().isSmartRedo()); + return madeChangesAtLeastOnce; } public ClassNode assureLoaded(String ref) { - WrappedClassNode clazz = classpath.get(ref); + ClassNode clazz = classpath.get(ref); + // Attempt to fetch from runtime, cases like 'java/awt/bla' can be loaded this way + if (clazz == null) { + clazz = pullFromRuntime(ref); + } + // Still missing, cannot recover if (clazz == null) { throw new NoClassInPathException(ref); } - return clazz.classNode; + return clazz; } public ClassNode assureLoadedElseRemove(String referencer, String ref) { - WrappedClassNode clazz = classpath.get(ref); + ClassNode clazz = classpath.get(ref); if (clazz == null) { classes.remove(referencer); classpath.remove(referencer); return null; } - return clazz.classNode; + return clazz; + } + + private ClassNode pullFromRuntime(String ref) { + try { + if (!missingRefs.contains(ref)) { + // Realistically we do not need the method bodies at all, can skip. + ClassNode node = new ClassNode(Opcodes.ASM9); + new ClassReader(ref).accept(node, ClassReader.SKIP_CODE); + classpath.put(ref, node); + return node; + } + } catch (IOException ex) { + // Ignored, plenty of cases where ref will not exist at runtime. + // Cache missing value so that we don't try again later. + missingRefs.add(ref); + } + return null; } public void loadHierachy() { Set processed = new HashSet<>(); - LinkedList toLoad = new LinkedList<>(); - toLoad.addAll(this.classes.values().stream().map(wrappedClassNode -> wrappedClassNode.classNode).collect(Collectors.toList())); + LinkedList toLoad = new LinkedList<>(this.classes.values()); while (!toLoad.isEmpty()) { for (ClassNode toProcess : loadHierachy(toLoad.poll())) { if (processed.add(toProcess.name)) { @@ -256,46 +549,50 @@ public void resetHierachy() { this.hierachy.clear(); } + private ClassTree getOrCreateClassTree(String name) { + return this.hierachy.computeIfAbsent(name, ClassTree::new); + } + public List loadHierachy(ClassNode specificNode) { if (specificNode.name.equals("java/lang/Object")) { return Collections.emptyList(); } - if ((specificNode.access & Opcodes.ACC_INTERFACE) != 0) { - getClassTree(specificNode.name).parentClasses.add("java/lang/Object"); - return Collections.emptyList(); - } List toProcess = new ArrayList<>(); - ClassTree thisTree = getClassTree(specificNode.name); + ClassTree thisTree = getOrCreateClassTree(specificNode.name); ClassNode superClass; - if (DELETE_USELESS_CLASSES) { + if (this.configuration.isDeleteUselessClasses()) { superClass = assureLoadedElseRemove(specificNode.name, specificNode.superName); - if (superClass == null) + if (superClass == null) { //It got removed return toProcess; - } else + } + } else { superClass = assureLoaded(specificNode.superName); + } if (superClass == null) { throw new IllegalArgumentException("Could not load " + specificNode.name); } - ClassTree superTree = getClassTree(superClass.name); + ClassTree superTree = getOrCreateClassTree(superClass.name); superTree.subClasses.add(specificNode.name); thisTree.parentClasses.add(superClass.name); toProcess.add(superClass); for (String interfaceReference : specificNode.interfaces) { ClassNode interfaceNode; - if (DELETE_USELESS_CLASSES) { + if (this.configuration.isDeleteUselessClasses()) { interfaceNode = assureLoadedElseRemove(specificNode.name, interfaceReference); - if (interfaceNode == null) + if (interfaceNode == null) { //It got removed return toProcess; - } else + } + } else { interfaceNode = assureLoaded(interfaceReference); + } if (interfaceNode == null) { throw new IllegalArgumentException("Could not load " + interfaceReference); } - ClassTree interfaceTree = getClassTree(interfaceReference); + ClassTree interfaceTree = getOrCreateClassTree(interfaceReference); interfaceTree.subClasses.add(specificNode.name); thisTree.parentClasses.add(interfaceReference); toProcess.add(interfaceNode); @@ -322,8 +619,9 @@ public boolean isSubclass(String possibleParent, String possibleChild) { layer.clear(); for (String r : clone) { ClassTree tree = hierachy.get(r); - if (tree != null) + if (tree != null) { layer.addAll(tree.subClasses); + } } } } @@ -333,9 +631,8 @@ public boolean isSubclass(String possibleParent, String possibleChild) { public ClassTree getClassTree(String classNode) { ClassTree tree = hierachy.get(classNode); if (tree == null) { - tree = new ClassTree(); - tree.thisClass = classNode; - hierachy.put(classNode, tree); + loadHierachyAll(assureLoaded(classNode)); + return getClassTree(classNode); } return tree; } @@ -348,50 +645,50 @@ public byte[] toByteArray(ClassNode node) { } }); } - ClassWriter writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS); + ClassWriter writer = new CustomClassWriter(ClassWriter.COMPUTE_FRAMES); try { - try { + node.accept(writer); + } catch (Throwable e) { + if (e instanceof NoClassInPathException) { + NoClassInPathException ex = (NoClassInPathException) e; + System.out.println("Error: " + ex.getClassName() + " could not be found while writing " + node.name + ". Using COMPUTE_MAXS"); + writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS); + node.accept(writer); + } else if (e instanceof NegativeArraySizeException || e instanceof ArrayIndexOutOfBoundsException) { + System.out.println("Error: failed to compute frames"); + writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS); node.accept(writer); - } catch (RuntimeException e) { - if (e instanceof NoClassInPathException) { - NoClassInPathException ex = (NoClassInPathException) e; - System.out.println("Error: " + ex.getClassName() + " could not be found while writing " + node.name + ". Using COMPUTE_MAXS"); + } else if (e.getMessage() != null) { + if (e.getMessage().contains("JSR/RET")) { + System.out.println("ClassNode contained JSR/RET so COMPUTE_MAXS instead"); writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS); node.accept(writer); - } else if (e.getMessage() != null) { - if (e.getMessage().contains("JSR/RET")) { - System.out.println("ClassNode contained JSR/RET so COMPUTE_MAXS instead"); - writer = new CustomClassWriter(ClassWriter.COMPUTE_MAXS); - node.accept(writer); - } else { - throw e; - } } else { - throw e; + System.out.println("Error while writing " + node.name); + e.printStackTrace(System.out); } + } else { + System.out.println("Error while writing " + node.name); + e.printStackTrace(System.out); } - byte[] classBytes = writer.toByteArray(); + } + byte[] classBytes = writer.toByteArray(); + if (configuration.isVerify()) { ClassReader cr = new ClassReader(classBytes); try { cr.accept(new CheckClassAdapter(new ClassWriter(0)), 0); } catch (Throwable t) { System.out.println("Error: " + node.name + " failed verification"); -// t.printStackTrace(System.out); + t.printStackTrace(System.out); } - return classBytes; - } catch (Throwable t) { - System.out.println("Error while writing " + node.name); - t.printStackTrace(System.out); } - return null; - } - public File getFile() { - return input; + return classBytes; } public class CustomClassWriter extends ClassWriter { + public CustomClassWriter(int flags) { super(flags); } @@ -437,8 +734,9 @@ private String getCommonSuperClass0(String type1, String type2) { } private boolean isAssignableFrom(String type1, String type2) { - if (type1.equals("java/lang/Object")) + if (type1.equals("java/lang/Object")) { return true; + } if (type1.equals(type2)) { return true; } @@ -446,8 +744,7 @@ private boolean isAssignableFrom(String type1, String type2) { assureLoaded(type2); ClassTree firstTree = getClassTree(type1); Set allChilds1 = new HashSet<>(); - LinkedList toProcess = new LinkedList<>(); - toProcess.addAll(firstTree.subClasses); + LinkedList toProcess = new LinkedList<>(firstTree.subClasses); while (!toProcess.isEmpty()) { String s = toProcess.poll(); if (allChilds1.add(s)) { @@ -459,5 +756,4 @@ private boolean isAssignableFrom(String type1, String type2) { return allChilds1.contains(type2); } } - } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/DeobfuscatorMain.java b/src/main/java/com/javadeobfuscator/deobfuscator/DeobfuscatorMain.java index 11b02281..dcaff2c1 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/DeobfuscatorMain.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/DeobfuscatorMain.java @@ -16,123 +16,119 @@ package com.javadeobfuscator.deobfuscator; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.javadeobfuscator.deobfuscator.config.Configuration; +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.config.TransformerConfigDeserializer; import com.javadeobfuscator.deobfuscator.exceptions.NoClassInPathException; import com.javadeobfuscator.deobfuscator.exceptions.PreventableStackOverflowError; -import com.javadeobfuscator.deobfuscator.transformers.Transformer; import org.apache.commons.cli.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; +import java.io.IOException; public class DeobfuscatorMain { - public static void main(String[] args) { + public static void main(String[] args) throws ClassNotFoundException { System.exit(run(args)); } - public static int run(String[] args) { + public static int run(String[] args) throws ClassNotFoundException { + Logger logger = LoggerFactory.getLogger(DeobfuscatorMain.class); + Options options = new Options(); - options.addOption("transformer", true, "A transformer to use"); - options.addOption("path", true, "A JAR to be placed in the classpath"); - options.addOption("input", true, "The input file"); - options.addOption("output", true, "The output file"); - //TODO: - // * keepClass - // * custom normalizer name - - CommandLineParser parser = new DefaultParser(); + options.addOption("c", "config", true, "The configuration file to use"); + + CommandLineParser cmdlineParser = new DefaultParser(); + CommandLine cmdLine; try { - Deobfuscator deobfuscator = new Deobfuscator(); - CommandLine cmd = parser.parse(options, args); + cmdLine = cmdlineParser.parse(options, args); + } catch (ParseException e) { + logger.error("An error occurred while parsing the commandline", e); + return 1; + } - if (!cmd.hasOption("input")) { - System.out.println("No input jar specified"); - return 3; - } + if (!cmdLine.hasOption("config")) { + logger.error("A config file must be specified"); + return 2; + } - if (!cmd.hasOption("output")) { - System.out.println("No output jar specified"); - return 4; - } + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()) + .registerModule( + new SimpleModule().addDeserializer(TransformerConfig.class, new TransformerConfigDeserializer(logger)) + ); - File input = new File(cmd.getOptionValue("input")); - if (!input.exists()) { - System.out.println("Input file does not exist"); - return 5; - } + Configuration configuration; + try { + configuration = mapper.readValue(new File(cmdLine.getOptionValue("config")), Configuration.class); + } catch (IOException e) { + logger.error("An error occurred while parsing the configuration file", e); + return 3; + } - File output = new File(cmd.getOptionValue("output")); - if (output.exists()) { - System.out.println("Warning! Output file already exists"); - } + if (configuration.getInput() == null) { + logger.error("An input JAR must be specified"); + return 4; + } - deobfuscator.withInput(input).withOutput(output); + if (configuration.isDetect()) { + return run(configuration); + } - String[] transformers = cmd.getOptionValues("transformer"); - if (transformers == null || transformers.length == 0) { - System.out.println("No transformers specified"); - return 2; - } - for (String transformer : transformers) { - Class clazz = null; - try { - clazz = Class.forName("com.javadeobfuscator.deobfuscator.transformers." + transformer); - } catch (ClassNotFoundException exception) { - try { - clazz = Class.forName(transformer); - } catch (ClassNotFoundException exception1) { - System.out.println("Could not locate transformer " + transformer); - } - } - if (clazz != null) { - if (Transformer.class.isAssignableFrom(clazz)) { - deobfuscator.withTransformer(clazz.asSubclass(Transformer.class)); - } else { - System.out.println(clazz.getCanonicalName() + " does not extend com.javadeobfuscator.deobfuscator.transformers.Transformer"); - } - } - } + if (configuration.getOutput() == null) { + logger.error("An output JAR must be specified"); + return 5; + } - String[] paths = cmd.getOptionValues("path"); - if (paths != null) { - for (String path : paths) { - File file = new File(path); - if (file.exists()) { - deobfuscator.withClasspath(file); - } else { - System.out.println("Could not find classpath file " + path); - } - } + if (configuration.getOutput().exists()) { + logger.warn("The specified output JAR already exists!"); + File parent = configuration.getOutput().getParentFile(); + if (!configuration.getOutput().renameTo(new File(parent, configuration.getOutput().getName() + ".bak"))) { + logger.warn("I was unable to back up the previous output JAR"); } + } - try { - deobfuscator.start(); - return 0; - } catch (NoClassInPathException ex) { - for (int i = 0; i < 5; i++) - System.out.println(); - System.out.println("** DO NOT OPEN AN ISSUE ON GITHUB **"); - System.out.println("Could not locate a class file."); - System.out.println("Have you added the necessary files to the -path argument?"); - System.out.println("The error was:"); - ex.printStackTrace(System.out); - return -2; - } catch (PreventableStackOverflowError ex) { - for (int i = 0; i < 5; i++) - System.out.println(); - System.out.println("** DO NOT OPEN AN ISSUE ON GITHUB **"); - System.out.println("A StackOverflowError occurred during deobfuscation, but it is preventable"); - System.out.println("Try increasing your stack size using the -Xss flag"); - System.out.println("The error was:"); - ex.printStackTrace(System.out); - return -3; - } catch (Throwable t) { - for (int i = 0; i < 5; i++) - System.out.println(); - System.out.println("Deobfuscation failed. Please open a ticket on GitHub and provide the following error:"); - t.printStackTrace(System.out); - return -1; - } - } catch (ParseException e) { - return 1; + if (configuration.getTransformers() == null || configuration.getTransformers().size() == 0) { + logger.error("At least one transformer must be specified"); + return 6; + } + + return run(configuration); + } + + private static int run(Configuration configuration) { + Deobfuscator deobfuscator = new Deobfuscator(configuration); + + try { + deobfuscator.start(); + return 0; + } catch (NoClassInPathException ex) { + for (int i = 0; i < 5; i++) + System.out.println(); + System.out.println("** DO NOT OPEN AN ISSUE ON GITHUB **"); + System.out.println("Could not locate a class file."); + System.out.println("Have you added the necessary files to the -path argument?"); + System.out.println("The error was:"); + ex.printStackTrace(System.out); + return -2; + } catch (PreventableStackOverflowError ex) { + for (int i = 0; i < 5; i++) + System.out.println(); + System.out.println("** DO NOT OPEN AN ISSUE ON GITHUB **"); + System.out.println("A StackOverflowError occurred during deobfuscation, but it is preventable"); + System.out.println("Try increasing your stack size using the -Xss flag"); + System.out.println("The error was:"); + ex.printStackTrace(System.out); + return -3; + } catch (Throwable t) { + for (int i = 0; i < 5; i++) + System.out.println(); + System.out.println("Deobfuscation failed. Please open a ticket on GitHub and provide the following error:"); + t.printStackTrace(System.out); + return -1; } } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/AnalyzerResult.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/AnalyzerResult.java index a691fcd9..617a6a47 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/AnalyzerResult.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/AnalyzerResult.java @@ -17,7 +17,7 @@ package com.javadeobfuscator.deobfuscator.analyzer; import com.javadeobfuscator.deobfuscator.analyzer.frame.Frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.AbstractInsnNode; import java.util.Collections; import java.util.HashMap; @@ -33,7 +33,8 @@ public class AnalyzerResult { } protected Map> frames; - private Map reverse; + protected Map mapping; + private Map reverse; protected int maxLocals; protected int maxStack; @@ -41,19 +42,22 @@ public Map> getFrames() { return frames; } - public AbstractInsnNode getInsnNode(Frame frame) { - if (reverse == null) { - reverse = new HashMap<>(); - frames.forEach((key, value) -> value.forEach(f -> reverse.put(f, key))); - } - - return reverse.get(frame); - } + public AbstractInsnNode getInsnNode(Frame frame) { + if (reverse == null) { + reverse = new HashMap<>(); + frames.forEach((key, value) -> value.forEach(f -> reverse.put(f, key))); + } + + return reverse.get(frame); + } public Map getMapping() { - Map reverseMapping = new HashMap<>(); - frames.entrySet().forEach(ent -> ent.getValue().forEach(frame -> reverseMapping.put(frame, ent.getKey()))); - return reverseMapping; + if (mapping == null) { + Map reverseMapping = new HashMap<>(); + frames.entrySet().forEach(ent -> ent.getValue().forEach(frame -> reverseMapping.put(frame, ent.getKey()))); + mapping = reverseMapping; + } + return mapping; } public int getMaxLocals() { diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/ArgsAnalyzer.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/ArgsAnalyzer.java index 119765e1..80b735f4 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/ArgsAnalyzer.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/ArgsAnalyzer.java @@ -3,220 +3,1137 @@ import java.util.ArrayList; import java.util.List; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.AbstractInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MultiANewArrayInsnNode; + import com.javadeobfuscator.deobfuscator.utils.Utils; /** - * This class is used to retrieve where a method's args begin/end. + * This class is used to retrieve where a method's args begin. * Somtimes, obfuscators don't like to put a method's args right before its method that uses it. - * This analyzer can only run backwards, because running forwards doesn't work. + * This analyzer can run both forwards and backwards. * @author ThisTestUser */ public class ArgsAnalyzer { - private final MethodNode method; - /** - * Which index do we start at? + * Which instruction do we start at? */ - private final int startIndex; + private final AbstractInsnNode start; /** - * How many arguments do we need? + * How many args do we need? */ private final int argSize; - public ArgsAnalyzer(MethodNode method, int startIndex, int argSize) + /** + * What direction should we run? + */ + private final Mode mode; + + /** + * Are there any opcodes which we should immedately stop execution if we pass through them? + */ + private final int[] stopCodes; + + /** + * The breakpoint is a "stop" node that tells the execution to immediately stop if reached. + * Note that any other data such as "difference" will be based on the last node analyzed. + */ + private AbstractInsnNode breakpoint; + + /** + * If true, the analyzer will end at the breakpoint (if it is reached) + */ + private boolean forceBreak; + + /** + * If true, backward analysis will return the first instance the stack reaches 0, even + * if the node requests more args. + */ + private boolean ignoreNeeded; + private boolean specialDup; + + /** + * If true, forward analysis will not stop the first time the arg reaches zero, only if the stack size is 0 + * AFTER a node. + */ + private boolean onlyZero; + + public ArgsAnalyzer(AbstractInsnNode start, int argSize, Mode mode, int... stopCodes) { - this.method = method; - this.startIndex = startIndex; + if(argSize < 0) + throw new IllegalArgumentException("Args needed cannot be negative"); + this.start = start; this.argSize = argSize; + this.mode = mode; + this.stopCodes = stopCodes; + } + + public void setBreakpoint(AbstractInsnNode breakpoint) + { + this.breakpoint = breakpoint; + } + + public void setForcebreak(boolean forceBreak) + { + this.forceBreak = forceBreak; + } + + public void setIgnoreNeeded(boolean ignoreNeeded) + { + this.ignoreNeeded = ignoreNeeded; + } + + public void setSpecialDup(boolean specialDup) + { + this.specialDup = specialDup; + } + + public void setOnlyZero(boolean onlyZero) + { + this.onlyZero = onlyZero; + } + + public Result lookupArgs() + { + if(mode == Mode.BACKWARDS) + return lookupArgsBackwards(); + else if(mode == Mode.FORWARDS) + return lookupArgsForwards(); + else + throw new IllegalArgumentException("Unknown mode"); + } + + /** + * Performs an arg lookup backwards. + * @return The first insn node where the argSize is 0. + */ + private Result lookupArgsBackwards() + { + AbstractInsnNode result = null; + int diff = 0; + int needed = argSize; + AbstractInsnNode ain = start; + AbstractInsnNode swap = null; + if(needed == 0 && !forceBreak) + return new Result(0, needed, ain, swap, null); + while(ain != null) + { + for(int code : stopCodes) + if(ain.getOpcode() == code) + return new FailedResult(diff, needed, ain, swap, null); + if(breakpoint == ain) + return new Result(diff, needed, ain, swap, null); + int prevNeeded = needed; + if((ain.getOpcode() >= Opcodes.ACONST_NULL && ain.getOpcode() <= Opcodes.ICONST_5) + || (ain.getOpcode() >= Opcodes.FCONST_0 && ain.getOpcode() <= Opcodes.FCONST_2) + || ain.getOpcode() == Opcodes.BIPUSH || ain.getOpcode() == Opcodes.SIPUSH) + needed--; + else if((ain.getOpcode() >= Opcodes.LCONST_0 && ain.getOpcode() <= Opcodes.LCONST_1) + || (ain.getOpcode() >= Opcodes.DCONST_0 && ain.getOpcode() <= Opcodes.DCONST_1)) + needed -= 2; + else if(ain.getOpcode() == Opcodes.LDC) + { + LdcInsnNode cst = (LdcInsnNode)ain; + if(cst.cst instanceof Double || cst.cst instanceof Long) + needed -= 2; + else + needed--; + }else if(ain.getOpcode() == Opcodes.ILOAD || ain.getOpcode() == Opcodes.ALOAD + || ain.getOpcode() == Opcodes.FLOAD) + needed--; + else if(ain.getOpcode() == Opcodes.DLOAD || ain.getOpcode() == Opcodes.LLOAD) + needed -= 2; + else if(ain.getOpcode() == Opcodes.ISTORE || ain.getOpcode() == Opcodes.FSTORE + || ain.getOpcode() == Opcodes.ASTORE) + needed++; + else if(ain.getOpcode() == Opcodes.DSTORE || ain.getOpcode() == Opcodes.LSTORE) + needed += 2; + else if(ain.getOpcode() == Opcodes.IALOAD || ain.getOpcode() == Opcodes.FALOAD + || ain.getOpcode() == Opcodes.AALOAD || ain.getOpcode() == Opcodes.BALOAD + || ain.getOpcode() == Opcodes.CALOAD || ain.getOpcode() == Opcodes.SALOAD) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 2; + }else if(ain.getOpcode() == Opcodes.LALOAD || ain.getOpcode() == Opcodes.DALOAD) + { + needed -= 2; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 2; + }else if(ain.getOpcode() == Opcodes.IASTORE || ain.getOpcode() == Opcodes.FASTORE + || ain.getOpcode() == Opcodes.AASTORE || ain.getOpcode() == Opcodes.BASTORE + || ain.getOpcode() == Opcodes.CASTORE || ain.getOpcode() == Opcodes.SASTORE) + needed += 3; + else if(ain.getOpcode() == Opcodes.LASTORE || ain.getOpcode() == Opcodes.DASTORE) + needed += 4; + else if(ain.getOpcode() == Opcodes.POP) + needed++; + else if(ain.getOpcode() == Opcodes.POP2) + needed += 2; + else if(ain.getOpcode() == Opcodes.DUP) + { + needed--; + if(specialDup && needed == 1) + { + diff = needed - prevNeeded; + result = ain; + break; + } + }else if(ain.getOpcode() == Opcodes.DUP_X1 && needed != 1 && needed != 2) + { + needed--; + if(needed == 2) + { + diff = needed - prevNeeded; + result = ain; + break; + } + }else if(ain.getOpcode() == Opcodes.DUP_X2 && needed != 1 && needed != 2 && needed != 3) + { + needed--; + if(needed == 3) + { + diff = needed - prevNeeded; + result = ain; + break; + } + }else if(ain.getOpcode() == Opcodes.DUP2 && needed != 2 && needed != 3) + { + needed -= 2; + if(needed == 2) + { + diff = needed - prevNeeded; + result = ain; + break; + } + }else if(ain.getOpcode() == Opcodes.DUP2_X1 && needed != 2 && needed != 3 && needed != 4) + { + needed -= 2; + if(needed == 3) + { + diff = needed - prevNeeded; + result = ain; + break; + } + }else if(ain.getOpcode() == Opcodes.DUP2_X2 && needed != 2 && needed != 3 && needed != 4 && needed != 5) + { + needed -= 2; + if(needed == 4) + { + diff = needed - prevNeeded; + result = ain; + break; + } + }else if(ain.getOpcode() == Opcodes.SWAP) + { + if(needed == 2) + { + if(swap != null) + return new FailedResult(diff, needed, ain, swap, null); + swap = ain; + needed--; + }else if(needed == 1) + { + if(swap != null) + return new FailedResult(diff, needed, ain, swap, null); + swap = ain; + needed++; + } + }else if(ain.getOpcode() == Opcodes.IADD || ain.getOpcode() == Opcodes.ISUB + || ain.getOpcode() == Opcodes.IMUL || ain.getOpcode() == Opcodes.IDIV + || ain.getOpcode() == Opcodes.IREM || ain.getOpcode() == Opcodes.ISHL + || ain.getOpcode() == Opcodes.ISHR || ain.getOpcode() == Opcodes.IUSHR + || ain.getOpcode() == Opcodes.IAND || ain.getOpcode() == Opcodes.IOR + || ain.getOpcode() == Opcodes.IXOR) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 2; + }else if(ain.getOpcode() == Opcodes.LADD || ain.getOpcode() == Opcodes.LSUB + || ain.getOpcode() == Opcodes.LMUL || ain.getOpcode() == Opcodes.LDIV + || ain.getOpcode() == Opcodes.LREM || ain.getOpcode() == Opcodes.LAND + || ain.getOpcode() == Opcodes.LOR || ain.getOpcode() == Opcodes.LXOR) + { + needed -= 2; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 4; + }else if(ain.getOpcode() == Opcodes.LSHL + || ain.getOpcode() == Opcodes.LSHR || ain.getOpcode() == Opcodes.LUSHR) + { + needed -= 2; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 3; + }else if(ain.getOpcode() == Opcodes.FADD || ain.getOpcode() == Opcodes.FSUB + || ain.getOpcode() == Opcodes.FMUL || ain.getOpcode() == Opcodes.FDIV + || ain.getOpcode() == Opcodes.FREM || ain.getOpcode() == Opcodes.FCMPL + || ain.getOpcode() == Opcodes.FCMPG) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 2; + }else if(ain.getOpcode() == Opcodes.DADD || ain.getOpcode() == Opcodes.DSUB + || ain.getOpcode() == Opcodes.DMUL || ain.getOpcode() == Opcodes.DDIV + || ain.getOpcode() == Opcodes.DREM) + { + needed -= 2; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 4; + }else if(ain.getOpcode() == Opcodes.LCMP || ain.getOpcode() == Opcodes.DCMPL + || ain.getOpcode() == Opcodes.DCMPG) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 4; + }else if(ain.getOpcode() == Opcodes.I2L || ain.getOpcode() == Opcodes.I2D + || ain.getOpcode() == Opcodes.F2L || ain.getOpcode() == Opcodes.F2D) + needed--; + else if(ain.getOpcode() == Opcodes.L2I || ain.getOpcode() == Opcodes.D2I + || ain.getOpcode() == Opcodes.L2F || ain.getOpcode() == Opcodes.D2F) + needed++; + else if(ain.getOpcode() == Opcodes.I2F || ain.getOpcode() == Opcodes.I2B + || ain.getOpcode() == Opcodes.I2C || ain.getOpcode() == Opcodes.I2S + || ain.getOpcode() == Opcodes.F2I || ain.getOpcode() == Opcodes.INEG + || ain.getOpcode() == Opcodes.FNEG) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed++; + }else if(ain.getOpcode() == Opcodes.L2D || ain.getOpcode() == Opcodes.D2L + || ain.getOpcode() == Opcodes.DNEG || ain.getOpcode() == Opcodes.LNEG) + { + needed -= 2; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += 2; + }else if(ain.getOpcode() == Opcodes.NEW) + needed--; + else if(ain.getOpcode() == Opcodes.GETSTATIC || ain.getOpcode() == Opcodes.GETFIELD) + { + if(Type.getType(((FieldInsnNode)ain).desc).getSort() == Type.DOUBLE + || Type.getType(((FieldInsnNode)ain).desc).getSort() == Type.LONG) + needed -= 2; + else + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + if(ain.getOpcode() == Opcodes.GETFIELD) + needed++; + }else if(ain.getOpcode() == Opcodes.PUTSTATIC || ain.getOpcode() == Opcodes.PUTFIELD) + { + if(Type.getType(((FieldInsnNode)ain).desc).getSort() == Type.DOUBLE + || Type.getType(((FieldInsnNode)ain).desc).getSort() == Type.LONG) + needed += 2; + else + needed++; + if(ain.getOpcode() == Opcodes.PUTFIELD) + needed++; + }else if(ain.getOpcode() == Opcodes.INVOKEVIRTUAL || ain.getOpcode() == Opcodes.INVOKESPECIAL + || ain.getOpcode() == Opcodes.INVOKEINTERFACE || ain.getOpcode() == Opcodes.INVOKESTATIC) + { + if(Type.getReturnType(((MethodInsnNode)ain).desc).getSort() == Type.DOUBLE + || Type.getReturnType(((MethodInsnNode)ain).desc).getSort() == Type.LONG) + needed -= 2; + else if(Type.getReturnType(((MethodInsnNode)ain).desc).getSort() != Type.VOID + && Type.getReturnType(((MethodInsnNode)ain).desc).getSort() != Type.METHOD) + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + if(ain.getOpcode() != Opcodes.INVOKESTATIC) + needed++; + for(Type t : Type.getArgumentTypes(((MethodInsnNode)ain).desc)) + { + if(t.getSort() == Type.LONG || t.getSort() == Type.DOUBLE) + needed += 2; + else + needed++; + } + }else if(ain.getOpcode() == Opcodes.INVOKEDYNAMIC) + { + if(Type.getReturnType(((InvokeDynamicInsnNode)ain).desc).getSort() == Type.DOUBLE + || Type.getReturnType(((InvokeDynamicInsnNode)ain).desc).getSort() == Type.LONG) + needed -= 2; + else if(Type.getReturnType(((InvokeDynamicInsnNode)ain).desc).getSort() != Type.VOID + && Type.getReturnType(((InvokeDynamicInsnNode)ain).desc).getSort() != Type.METHOD) + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + for(Type t : Type.getArgumentTypes(((InvokeDynamicInsnNode)ain).desc)) + { + if(t.getSort() == Type.LONG || t.getSort() == Type.DOUBLE) + needed += 2; + else + needed++; + } + }else if((ain.getOpcode() >= Opcodes.IFEQ && ain.getOpcode() <= Opcodes.IFLE) + || ain.getOpcode() == Opcodes.IFNULL || ain.getOpcode() == Opcodes.IFNONNULL + || ain.getOpcode() == Opcodes.TABLESWITCH || ain.getOpcode() == Opcodes.LOOKUPSWITCH + || ain.getOpcode() == Opcodes.ATHROW || ain.getOpcode() == Opcodes.RET) + return new FailedResult(diff, needed, ain, swap, null); + else if(ain.getOpcode() >= Opcodes.IF_ICMPEQ && ain.getOpcode() <= Opcodes.IF_ACMPNE) + return new FailedResult(diff, needed, ain, swap, null); + else if(ain.getOpcode() == Opcodes.GOTO) + return new FailedResult(diff, needed, ain, swap, null); + else if(ain.getOpcode() == Opcodes.MULTIANEWARRAY) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed += ((MultiANewArrayInsnNode)ain).dims; + }else if(ain.getOpcode() == Opcodes.INSTANCEOF) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed++; + }else if(ain.getOpcode() == Opcodes.CHECKCAST) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed++; + }else if(ain.getOpcode() == Opcodes.NEWARRAY || ain.getOpcode() == Opcodes.ANEWARRAY + || ain.getOpcode() == Opcodes.ARRAYLENGTH) + { + needed--; + if(needed <= 0 && ignoreNeeded) + { + diff = needed - prevNeeded; + result = ain; + break; + } + needed++; + }else if(ain.getOpcode() == Opcodes.MONITORENTER || ain.getOpcode() == Opcodes.MONITOREXIT) + needed++; + else if(ain.getOpcode() == Opcodes.IRETURN || ain.getOpcode() == Opcodes.FRETURN + || ain.getOpcode() == Opcodes.ARETURN) + return new FailedResult(diff, needed, ain, swap, null); + else if(ain.getOpcode() == Opcodes.LRETURN || ain.getOpcode() == Opcodes.DRETURN) + return new FailedResult(diff, needed, ain, swap, null); + else if(ain.getOpcode() == Opcodes.RETURN) + return new FailedResult(diff, needed, ain, swap, null); + if(Utils.isInstruction(ain)) + diff = needed - prevNeeded; + if(needed <= 0 && !forceBreak) + { + result = ain; + break; + } + ain = ain.getPrevious(); + } + return new Result(diff, needed, result, swap, null); } /** - * Performs an arg lookup. - * @return The list of nodes as the arguments, in order. + * Performs an arg lookup forwards. + * @return The first insn node where the argSize is 0. */ - public List lookupArgs() + private Result lookupArgsForwards() { - //Reverse bytecode analysis - List stack = new ArrayList<>(); - int neededNodes = 0; - //This isn't complete, fix as new errors come - for(int i = startIndex; i >= 0; i--) - { - AbstractInsnNode node = method.instructions.get(i); - if(Utils.isNumber(node)) - { - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - }else if(node.getOpcode() == Opcodes.ACONST_NULL - || node.getOpcode() == Opcodes.LDC) - { - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - }else if(node.getOpcode() >= Opcodes.ILOAD - && node.getOpcode() <= Opcodes.ALOAD) - { - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - }else if(node.getOpcode() >= Opcodes.ISTORE - && node.getOpcode() <= Opcodes.ASTORE) - neededNodes++; - else if(node.getOpcode() == Opcodes.INVOKEVIRTUAL - || node.getOpcode() == Opcodes.INVOKESPECIAL - || node.getOpcode() == Opcodes.INVOKEINTERFACE) - { - //Note that we add one for the instance - MethodInsnNode cast = (MethodInsnNode)node; - neededNodes += Type.getArgumentTypes(cast.desc).length + 1; - if(Type.getReturnType(cast.desc).getReturnType().getSort() > 0 - && Type.getReturnType(cast.desc).getReturnType().getSort() < 11) - { - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - } - }else if(node.getOpcode() == Opcodes.INVOKESTATIC) - { - MethodInsnNode cast = (MethodInsnNode)node; - neededNodes += Type.getArgumentTypes(cast.desc).length; - if(Type.getReturnType(cast.desc).getReturnType().getSort() > 0 - && Type.getReturnType(cast.desc).getReturnType().getSort() < 11) - { - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - } - }else if(node.getOpcode() == Opcodes.GETSTATIC || node.getOpcode() == Opcodes.GETFIELD) - { - if(node.getOpcode() == Opcodes.GETFIELD) - neededNodes++; - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - }else if(node.getOpcode() == Opcodes.PUTFIELD || node.getOpcode() == Opcodes.PUTSTATIC) - { - if(node.getOpcode() == Opcodes.PUTFIELD) - neededNodes++; - neededNodes++; - }else if(node.getOpcode() == Opcodes.INVOKEDYNAMIC) - { - //InvokeDynamic adds to the stack - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - }else if(node.getOpcode() == Opcodes.NEW) - { - if(node.getNext() != null - && node.getNext().getOpcode() == Opcodes.DUP) - { - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - } - stack.add(0, node); - if(neededNodes > 0) - { - neededNodes--; - stack.remove(0); - } - if(stack.size() >= argSize && neededNodes == 0) - { - if(stack.size() > argSize) - throw new RuntimeException("Recieved an arg size of " + stack.size() + " but expected " - + argSize + " !"); - return stack; - } - } - } - return null; + List skippedDups = new ArrayList<>(); + AbstractInsnNode result = null; + int diff = 0; + int stackSize = argSize; + AbstractInsnNode ain = start; + AbstractInsnNode swap = null; + if(stackSize == 0 && !forceBreak) + return new Result(0, stackSize, ain, swap, skippedDups); + while(ain != null) + { + for(int code : stopCodes) + if(ain.getOpcode() == code) + return new FailedResult(diff, stackSize, ain, swap, skippedDups); + if(breakpoint == ain) + return new Result(diff, -stackSize, ain, swap, skippedDups); + int prevNeeded = stackSize; + if((ain.getOpcode() >= Opcodes.ACONST_NULL && ain.getOpcode() <= Opcodes.ICONST_5) + || (ain.getOpcode() >= Opcodes.FCONST_0 && ain.getOpcode() <= Opcodes.FCONST_2) + || ain.getOpcode() == Opcodes.BIPUSH || ain.getOpcode() == Opcodes.SIPUSH) + stackSize++; + else if((ain.getOpcode() >= Opcodes.LCONST_0 && ain.getOpcode() <= Opcodes.LCONST_1) + || (ain.getOpcode() >= Opcodes.DCONST_0 && ain.getOpcode() <= Opcodes.DCONST_1)) + stackSize += 2; + else if(ain.getOpcode() == Opcodes.LDC) + { + LdcInsnNode cst = (LdcInsnNode)ain; + if(cst.cst instanceof Double || cst.cst instanceof Long) + stackSize += 2; + else + stackSize++; + }else if(ain.getOpcode() == Opcodes.ILOAD || ain.getOpcode() == Opcodes.ALOAD + || ain.getOpcode() == Opcodes.FLOAD) + stackSize++; + else if(ain.getOpcode() == Opcodes.DLOAD || ain.getOpcode() == Opcodes.LLOAD) + stackSize += 2; + else if(ain.getOpcode() == Opcodes.ISTORE || ain.getOpcode() == Opcodes.FSTORE + || ain.getOpcode() == Opcodes.ASTORE) + stackSize--; + else if(ain.getOpcode() == Opcodes.DSTORE || ain.getOpcode() == Opcodes.LSTORE) + stackSize -= 2; + else if(ain.getOpcode() == Opcodes.IALOAD || ain.getOpcode() == Opcodes.FALOAD + || ain.getOpcode() == Opcodes.AALOAD || ain.getOpcode() == Opcodes.BALOAD + || ain.getOpcode() == Opcodes.CALOAD || ain.getOpcode() == Opcodes.SALOAD) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.LALOAD || ain.getOpcode() == Opcodes.DALOAD) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(ain.getOpcode() == Opcodes.IASTORE || ain.getOpcode() == Opcodes.FASTORE + || ain.getOpcode() == Opcodes.AASTORE || ain.getOpcode() == Opcodes.BASTORE + || ain.getOpcode() == Opcodes.CASTORE || ain.getOpcode() == Opcodes.SASTORE) + stackSize -= 3; + else if(ain.getOpcode() == Opcodes.LASTORE || ain.getOpcode() == Opcodes.DASTORE) + stackSize -= 4; + else if(ain.getOpcode() == Opcodes.POP) + stackSize--; + else if(ain.getOpcode() == Opcodes.POP2) + stackSize -= 2; + else if(ain.getOpcode() == Opcodes.DUP) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(ain.getOpcode() == Opcodes.DUP_X1) + { + if(stackSize > 2) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(stackSize == 1) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + }else + skippedDups.add(ain); + }else if(ain.getOpcode() == Opcodes.DUP_X2) + { + if(stackSize > 3) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(stackSize == 1) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + }else + skippedDups.add(ain); + }else if(ain.getOpcode() == Opcodes.DUP2) + { + if(stackSize > 2) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 4; + }else + { + stackSize -= 2; + diff = stackSize - prevNeeded; + result = ain; + break; + } + }else if(ain.getOpcode() == Opcodes.DUP2_X1) + { + if(stackSize > 3) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 4; + }else if(stackSize == 1 || stackSize == 2) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + }else + skippedDups.add(ain); + }else if(ain.getOpcode() == Opcodes.DUP2_X2) + { + if(stackSize > 4) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 4; + }else if(stackSize == 1 || stackSize == 2) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + }else + skippedDups.add(ain); + }else if(ain.getOpcode() == Opcodes.SWAP) + { + if(stackSize == 2) + { + if(swap != null) + return new FailedResult(stackSize - prevNeeded, stackSize, ain, swap, skippedDups); + swap = ain; + stackSize--; + }else if(stackSize == 1) + { + if(swap != null) + return new FailedResult(stackSize - prevNeeded, stackSize, ain, swap, skippedDups); + swap = ain; + stackSize++; + } + }else if(ain.getOpcode() == Opcodes.IADD || ain.getOpcode() == Opcodes.ISUB + || ain.getOpcode() == Opcodes.IMUL || ain.getOpcode() == Opcodes.IDIV + || ain.getOpcode() == Opcodes.IREM || ain.getOpcode() == Opcodes.ISHL + || ain.getOpcode() == Opcodes.ISHR || ain.getOpcode() == Opcodes.IUSHR + || ain.getOpcode() == Opcodes.IAND || ain.getOpcode() == Opcodes.IOR + || ain.getOpcode() == Opcodes.IXOR) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.LADD || ain.getOpcode() == Opcodes.LSUB + || ain.getOpcode() == Opcodes.LMUL || ain.getOpcode() == Opcodes.LDIV + || ain.getOpcode() == Opcodes.LREM || ain.getOpcode() == Opcodes.LAND + || ain.getOpcode() == Opcodes.LOR || ain.getOpcode() == Opcodes.LXOR) + { + stackSize -= 4; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(ain.getOpcode() == Opcodes.LSHL + || ain.getOpcode() == Opcodes.LSHR || ain.getOpcode() == Opcodes.LUSHR) + { + stackSize -= 3; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(ain.getOpcode() == Opcodes.FADD || ain.getOpcode() == Opcodes.FSUB + || ain.getOpcode() == Opcodes.FMUL || ain.getOpcode() == Opcodes.FDIV + || ain.getOpcode() == Opcodes.FREM || ain.getOpcode() == Opcodes.FCMPL + || ain.getOpcode() == Opcodes.FCMPG) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.DADD || ain.getOpcode() == Opcodes.DSUB + || ain.getOpcode() == Opcodes.DMUL || ain.getOpcode() == Opcodes.DDIV + || ain.getOpcode() == Opcodes.DREM) + { + stackSize -= 4; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(ain.getOpcode() == Opcodes.LCMP || ain.getOpcode() == Opcodes.DCMPL + || ain.getOpcode() == Opcodes.DCMPG) + { + stackSize -= 4; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.I2L || ain.getOpcode() == Opcodes.I2D + || ain.getOpcode() == Opcodes.F2L || ain.getOpcode() == Opcodes.F2D) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(ain.getOpcode() == Opcodes.L2I || ain.getOpcode() == Opcodes.D2I + || ain.getOpcode() == Opcodes.L2F || ain.getOpcode() == Opcodes.D2F) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.I2F || ain.getOpcode() == Opcodes.I2B + || ain.getOpcode() == Opcodes.I2C || ain.getOpcode() == Opcodes.I2S + || ain.getOpcode() == Opcodes.F2I || ain.getOpcode() == Opcodes.INEG + || ain.getOpcode() == Opcodes.FNEG) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.L2D || ain.getOpcode() == Opcodes.D2L + || ain.getOpcode() == Opcodes.DNEG || ain.getOpcode() == Opcodes.LNEG) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize += 2; + }else if(ain.getOpcode() == Opcodes.NEW) + stackSize++; + else if(ain.getOpcode() == Opcodes.GETSTATIC || ain.getOpcode() == Opcodes.GETFIELD) + { + if(ain.getOpcode() == Opcodes.GETFIELD) + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + if(Type.getType(((FieldInsnNode)ain).desc).getSort() == Type.DOUBLE + || Type.getType(((FieldInsnNode)ain).desc).getSort() == Type.LONG) + stackSize += 2; + else + stackSize++; + }else if(ain.getOpcode() == Opcodes.PUTSTATIC || ain.getOpcode() == Opcodes.PUTFIELD) + { + if(ain.getOpcode() == Opcodes.PUTFIELD) + stackSize--; + if(Type.getType(((FieldInsnNode)ain).desc).getSort() == Type.DOUBLE + || Type.getType(((FieldInsnNode)ain).desc).getSort() == Type.LONG) + stackSize -= 2; + else + stackSize--; + }else if(ain.getOpcode() == Opcodes.INVOKEVIRTUAL || ain.getOpcode() == Opcodes.INVOKESPECIAL + || ain.getOpcode() == Opcodes.INVOKEINTERFACE || ain.getOpcode() == Opcodes.INVOKESTATIC) + { + if(ain.getOpcode() != Opcodes.INVOKESTATIC) + stackSize--; + for(Type t : Type.getArgumentTypes(((MethodInsnNode)ain).desc)) + { + if(t.getSort() == Type.LONG || t.getSort() == Type.DOUBLE) + stackSize -= 2; + else + stackSize--; + } + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + if(Type.getReturnType(((MethodInsnNode)ain).desc).getSort() == Type.DOUBLE + || Type.getReturnType(((MethodInsnNode)ain).desc).getSort() == Type.LONG) + stackSize += 2; + else if(Type.getReturnType(((MethodInsnNode)ain).desc).getSort() != Type.VOID + && Type.getReturnType(((MethodInsnNode)ain).desc).getSort() != Type.METHOD) + stackSize++; + }else if(ain.getOpcode() == Opcodes.INVOKEDYNAMIC) + { + for(Type t : Type.getArgumentTypes(((InvokeDynamicInsnNode)ain).desc)) + { + if(t.getSort() == Type.LONG || t.getSort() == Type.DOUBLE) + stackSize -= 2; + else + stackSize--; + } + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + if(Type.getReturnType(((InvokeDynamicInsnNode)ain).desc).getSort() == Type.DOUBLE + || Type.getReturnType(((InvokeDynamicInsnNode)ain).desc).getSort() == Type.LONG) + stackSize += 2; + else if(Type.getReturnType(((InvokeDynamicInsnNode)ain).desc).getSort() != Type.VOID + && Type.getReturnType(((InvokeDynamicInsnNode)ain).desc).getSort() != Type.METHOD) + stackSize++; + }else if((ain.getOpcode() >= Opcodes.IFEQ && ain.getOpcode() <= Opcodes.IFLE) + || ain.getOpcode() == Opcodes.IFNULL || ain.getOpcode() == Opcodes.IFNONNULL + || ain.getOpcode() == Opcodes.TABLESWITCH || ain.getOpcode() == Opcodes.LOOKUPSWITCH + || ain.getOpcode() == Opcodes.ATHROW || ain.getOpcode() == Opcodes.RET) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + return new FailedResult(stackSize - prevNeeded, stackSize, ain, swap, skippedDups); + }else if(ain.getOpcode() >= Opcodes.IF_ICMPEQ && ain.getOpcode() <= Opcodes.IF_ACMPNE) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + return new FailedResult(stackSize - prevNeeded, stackSize, ain, swap, skippedDups); + }else if(ain.getOpcode() == Opcodes.INSTANCEOF) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.CHECKCAST) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.GOTO) + return new FailedResult(diff, stackSize, ain, swap, skippedDups); + else if(ain.getOpcode() == Opcodes.MULTIANEWARRAY) + { + stackSize -= ((MultiANewArrayInsnNode)ain).dims; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.NEWARRAY || ain.getOpcode() == Opcodes.ANEWARRAY + || ain.getOpcode() == Opcodes.ARRAYLENGTH) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + stackSize++; + }else if(ain.getOpcode() == Opcodes.MONITORENTER || ain.getOpcode() == Opcodes.MONITOREXIT) + stackSize--; + else if(ain.getOpcode() == Opcodes.IRETURN || ain.getOpcode() == Opcodes.FRETURN + || ain.getOpcode() == Opcodes.ARETURN) + { + stackSize--; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + return new FailedResult(stackSize - prevNeeded, stackSize, ain, swap, skippedDups); + }else if(ain.getOpcode() == Opcodes.LRETURN || ain.getOpcode() == Opcodes.DRETURN) + { + stackSize -= 2; + if(stackSize <= 0 && !forceBreak && !onlyZero) + { + diff = stackSize - prevNeeded; + result = ain; + break; + } + return new FailedResult(stackSize - prevNeeded, stackSize, ain, swap, skippedDups); + }else if(ain.getOpcode() == Opcodes.RETURN) + return new FailedResult(diff, stackSize, ain, swap, skippedDups); + if(Utils.isInstruction(ain)) + diff = stackSize - prevNeeded; + if(stackSize <= 0 && !forceBreak) + { + result = ain; + break; + } + ain = ain.getNext(); + } + return new Result(diff, -stackSize, result, swap, skippedDups); + } + + public enum Mode + { + FORWARDS, + BACKWARDS; + } + + public static class Result + { + private final int diff; + private final int argsNeeded; + private final AbstractInsnNode firstArgInsn; + private final AbstractInsnNode swap; + private final List skippedDups; + + public Result(int diff, int argsNeeded, AbstractInsnNode firstArgInsn, + AbstractInsnNode swap, List skippedDups) + { + this.diff = diff; + this.argsNeeded = argsNeeded; + this.firstArgInsn = firstArgInsn; + this.swap = swap; + this.skippedDups = skippedDups; + } + + public int getDiff() + { + return diff; + } + + public int getArgsNeeded() + { + return argsNeeded; + } + + public AbstractInsnNode getFirstArgInsn() + { + return firstArgInsn; + } + + public AbstractInsnNode getSwap() + { + return swap; + } + + public List getSkippedDups() + { + return skippedDups; + } + } + + public static class FailedResult extends Result + { + private final int diff; + private final int extraArgs; + private final AbstractInsnNode failedPoint; + private final AbstractInsnNode swap; + private final List skippedDups; + + public FailedResult(int diff, int extraArgs, AbstractInsnNode failedPoint, AbstractInsnNode swap, List skippedDups) + { + super(-1, -1, null, null, null); + this.diff = diff; + this.extraArgs = extraArgs; + this.failedPoint = failedPoint; + this.swap = swap; + this.skippedDups = skippedDups; + } + + public AbstractInsnNode getFailedPoint() + { + return failedPoint; + } + + public int getLastSuccessfulNodeDiff() + { + return diff; + } + + public int getExtraArgs() + { + return extraArgs; + } + + public AbstractInsnNode getSwapAtFailure() + { + return swap; + } + + public List getSkippedDupsAtFailure() + { + return skippedDups; + } + + @Override + public int getDiff() + { + throw new RuntimeException("ArgsAnalyzer lookup failed"); + } + + @Override + public int getArgsNeeded() + { + throw new RuntimeException("ArgsAnalyzer lookup failed"); + } + + @Override + public AbstractInsnNode getFirstArgInsn() + { + throw new RuntimeException("ArgsAnalyzer lookup failed"); + } + + @Override + public AbstractInsnNode getSwap() + { + throw new RuntimeException("ArgsAnalyzer lookup failed"); + } + + @Override + public List getSkippedDups() + { + throw new RuntimeException("ArgsAnalyzer lookup failed"); + } } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/FlowAnalyzer.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/FlowAnalyzer.java new file mode 100644 index 00000000..cef394e9 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/FlowAnalyzer.java @@ -0,0 +1,273 @@ +package com.javadeobfuscator.deobfuscator.analyzer; + +import java.util.*; +import java.util.Map.Entry; + +import org.apache.commons.lang3.tuple.Triple; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TableSwitchInsnNode; +import org.objectweb.asm.tree.TryCatchBlockNode; + +/** + * Flow Analyzer is a tool used for determining the flow of the code, using each label as a "block". + * @author ThisTestUser + */ +public class FlowAnalyzer +{ + private final MethodNode method; + public static final LabelNode ABSENT = new LabelNode(); + + public FlowAnalyzer(MethodNode method) + { + this.method = method; + } + + public Result analyze() + { + LinkedHashMap, List>>> labels = new LinkedHashMap<>(); + LinkedHashMap> trycatchMap = new LinkedHashMap<>(); + LabelNode currentLabel = null; + boolean hit = false; + labels.putIfAbsent(ABSENT, new AbstractMap.SimpleEntry<>(new ArrayList<>(), new ArrayList<>())); + List trycatchNow = new ArrayList<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + { + if(ain.getOpcode() == Opcodes.RET || ain.getOpcode() == Opcodes.JSR) + throw new RuntimeException("JSR/RET not supported"); + if(ain instanceof LabelNode) + { + if(!hit) + if(currentLabel == null) + labels.get(ABSENT).getValue().add(Triple.of((LabelNode)ain, new JumpData(JumpCause.NEXT, null), 0)); + else + labels.get(currentLabel).getValue().add(Triple.of((LabelNode)ain, new JumpData(JumpCause.NEXT, null), 0)); + hit = false; + currentLabel = (LabelNode)ain; + labels.putIfAbsent(currentLabel, new AbstractMap.SimpleEntry<>(new ArrayList<>(), new ArrayList<>())); + for(TryCatchBlockNode tcbn : method.tryCatchBlocks) + { + if(tcbn.start == currentLabel) + { + TryCatchBlockNode insertBefore = null; + for(TryCatchBlockNode tcbn2 : trycatchNow) + if(method.tryCatchBlocks.indexOf(tcbn2) > method.tryCatchBlocks.indexOf(tcbn)) + { + insertBefore = tcbn2; + break; + } + if(insertBefore == null) + trycatchNow.add(tcbn); + else + trycatchNow.add(trycatchNow.indexOf(insertBefore), tcbn); + } + if(tcbn.end == currentLabel) + trycatchNow.remove(tcbn); + } + if(currentLabel != null) + trycatchMap.put(currentLabel, new ArrayList<>(trycatchNow)); + continue; + } + if(ain.getOpcode() == Opcodes.GOTO || ain.getOpcode() == Opcodes.TABLESWITCH || ain.getOpcode() == Opcodes.LOOKUPSWITCH + || (ain.getOpcode() >= Opcodes.IRETURN && ain.getOpcode() <= Opcodes.RETURN) || ain.getOpcode() == Opcodes.ATHROW) + hit = true; + if(currentLabel == null) + labels.get(ABSENT).getKey().add(ain); + else + labels.get(currentLabel).getKey().add(ain); + } + loop: + for(Entry, List>>> entry : labels.entrySet()) + for(AbstractInsnNode ain : entry.getValue().getKey()) + if(ain.getOpcode() == Opcodes.GOTO) + { + entry.getValue().getValue().add(Triple.of(((JumpInsnNode)ain).label, new JumpData(JumpCause.GOTO, ain), 0)); + continue loop; + }else if(ain instanceof JumpInsnNode) + entry.getValue().getValue().add(Triple.of(((JumpInsnNode)ain).label, new JumpData(JumpCause.CONDITIONAL, ain), 0)); + else if(ain instanceof TableSwitchInsnNode || ain instanceof LookupSwitchInsnNode) + { + List jumps = ain instanceof TableSwitchInsnNode ? ((TableSwitchInsnNode)ain).labels : + ((LookupSwitchInsnNode)ain).labels; + LabelNode dflt = ain instanceof TableSwitchInsnNode ? ((TableSwitchInsnNode)ain).dflt : + ((LookupSwitchInsnNode)ain).dflt; + int i = 0; + for(LabelNode label : jumps) + { + entry.getValue().getValue().add(Triple.of(label, new JumpData(JumpCause.SWITCH, ain), i)); + i++; + } + entry.getValue().getValue().add(Triple.of(dflt, new JumpData(JumpCause.SWITCH, ain), i)); + continue loop; + }else if((ain.getOpcode() >= Opcodes.IRETURN && ain.getOpcode() <= Opcodes.RETURN) + || ain.getOpcode() == Opcodes.ATHROW) + continue loop; + for(Entry, List>>> entry : labels.entrySet()) + { + if(!entry.getValue().getValue().isEmpty() && entry.getValue().getValue().get(0).getMiddle().cause == JumpCause.NEXT) + entry.getValue().getValue().add(entry.getValue().getValue().remove(0)); + } + return new Result(labels, trycatchMap); + } + + /** + * Analyzers all the instructions passed from a starting point. + * @param start + * @param breaks + * @return + */ + public LinkedHashMap> analyze(AbstractInsnNode start, List breaks, + Map> switchBreaks, boolean includeBreaks, boolean includeTryCatch) + { + LabelNode currentLabel = ABSENT; + AbstractInsnNode prev = start; + while(prev != null) + { + if(prev instanceof LabelNode) + { + currentLabel = (LabelNode)prev; + break; + } + prev = prev.getPrevious(); + } + //Calculate try-catches + LabelNode lbl = ABSENT; + LinkedHashMap> trycatchMap = new LinkedHashMap<>(); + trycatchMap.put(ABSENT, new ArrayList<>()); + List trycatchNow = new ArrayList<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + if(ain instanceof LabelNode) + { + lbl = (LabelNode)ain; + //Note: Empty try-catches are not added + for(TryCatchBlockNode tc : method.tryCatchBlocks) + { + if(tc.start == lbl) + { + TryCatchBlockNode insertBefore = null; + for(TryCatchBlockNode tcbn2 : trycatchNow) + if(method.tryCatchBlocks.indexOf(tcbn2) > method.tryCatchBlocks.indexOf(tc)) + { + insertBefore = tcbn2; + break; + } + if(insertBefore == null) + trycatchNow.add(tc); + else + trycatchNow.add(trycatchNow.indexOf(insertBefore), tc); + } + if(tc.end == lbl) + trycatchNow.remove(tc); + } + trycatchMap.put(lbl, new ArrayList<>(trycatchNow)); + } + LinkedHashMap> result = new LinkedHashMap<>(); + //Recursive iteration through next + analyze0(trycatchMap, result, currentLabel, start, breaks, switchBreaks, includeBreaks, includeTryCatch); + return result; + } + + public void analyze0(LinkedHashMap> trycatchMap, + LinkedHashMap> result, + LabelNode currentLabel, AbstractInsnNode start, List breaks, Map> switchBreaks, + boolean includeBreaks, boolean includeTryCatch) + { + if(start != currentLabel && !breaks.contains(start)) + { + result.put(currentLabel, new ArrayList<>()); + if(includeTryCatch) + for(TryCatchBlockNode trycatch : trycatchMap.get(currentLabel)) + analyze0(trycatchMap, result, trycatch.handler, trycatch.handler, breaks, switchBreaks, includeBreaks, includeTryCatch); + } + while(start != null) + { + if(breaks.contains(start)) + { + if(start instanceof LabelNode && includeBreaks) + result.putIfAbsent((LabelNode)start, new ArrayList<>()); + else if(includeBreaks) + { + result.putIfAbsent(currentLabel, new ArrayList<>()); + result.get(currentLabel).add(start); + } + return; + } + if(start instanceof LabelNode) + { + if(result.containsKey(start)) + return; + currentLabel = (LabelNode)start; + result.put(currentLabel, new ArrayList<>()); + //Try-catch + if(includeTryCatch) + for(TryCatchBlockNode trycatch : trycatchMap.get(currentLabel)) + analyze0(trycatchMap, result, trycatch.handler, trycatch.handler, breaks, switchBreaks, includeBreaks, + includeTryCatch); + start = start.getNext(); + continue; + } + result.get(currentLabel).add(start); + if(start.getOpcode() == Opcodes.GOTO) + { + analyze0(trycatchMap, result, ((JumpInsnNode)start).label, ((JumpInsnNode)start).label, breaks, switchBreaks, + includeBreaks, includeTryCatch); + return; + }else if(start instanceof JumpInsnNode) + analyze0(trycatchMap, result, ((JumpInsnNode)start).label, ((JumpInsnNode)start).label, breaks, switchBreaks, + includeBreaks, includeTryCatch); + else if(start instanceof TableSwitchInsnNode || start instanceof LookupSwitchInsnNode) + { + List jumps = start instanceof TableSwitchInsnNode ? ((TableSwitchInsnNode)start).labels : + ((LookupSwitchInsnNode)start).labels; + LabelNode dflt = start instanceof TableSwitchInsnNode ? ((TableSwitchInsnNode)start).dflt : + ((LookupSwitchInsnNode)start).dflt; + for(LabelNode lbl : jumps) + if(!switchBreaks.containsKey(start) || switchBreaks.get(start) != lbl) + analyze0(trycatchMap, result, lbl, lbl, breaks, switchBreaks, includeBreaks, includeTryCatch); + if(!switchBreaks.containsKey(start) || switchBreaks.get(start) != dflt) + analyze0(trycatchMap, result, dflt, dflt, breaks, switchBreaks, includeBreaks, includeTryCatch); + return; + }else if((start.getOpcode() >= Opcodes.IRETURN && start.getOpcode() <= Opcodes.RETURN) + || start.getOpcode() == Opcodes.ATHROW) + return; + start = start.getNext(); + } + } + + public static enum JumpCause + { + NEXT, + GOTO, + CONDITIONAL, + SWITCH, + } + + public static class JumpData + { + public final JumpCause cause; + public final AbstractInsnNode ain; + + public JumpData(JumpCause cause, AbstractInsnNode ain) + { + this.cause = cause; + this.ain = ain; + } + } + + public static class Result + { + public final LinkedHashMap, List>>> labels; + public final LinkedHashMap> trycatchMap; + + public Result(LinkedHashMap, List>>> labels, + LinkedHashMap> trycatchMap) + { + this.labels = labels; + this.trycatchMap = trycatchMap; + } + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/MethodAnalyzer.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/MethodAnalyzer.java index dc6cced0..ab7fbab4 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/MethodAnalyzer.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/MethodAnalyzer.java @@ -25,26 +25,26 @@ import com.javadeobfuscator.deobfuscator.analyzer.frame.SwitchFrame; import com.javadeobfuscator.deobfuscator.analyzer.frame.ThrowFrame; import com.javadeobfuscator.deobfuscator.exceptions.PreventableStackOverflowError; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.AbstractInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.ClassNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.FieldInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.IincInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.IntInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.InvokeDynamicInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.JumpInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.LdcInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.LookupSwitchInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MultiANewArrayInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.TableSwitchInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.TryCatchBlockNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.TypeInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.VarInsnNode; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.IincInsnNode; +import org.objectweb.asm.tree.IntInsnNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.MultiANewArrayInsnNode; +import org.objectweb.asm.tree.TableSwitchInsnNode; +import org.objectweb.asm.tree.TryCatchBlockNode; +import org.objectweb.asm.tree.TypeInsnNode; +import org.objectweb.asm.tree.VarInsnNode; import com.javadeobfuscator.deobfuscator.utils.PrimitiveUtils; -import com.javadeobfuscator.deobfuscator.utils.Utils; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; import java.lang.reflect.Modifier; import java.util.AbstractMap; @@ -57,163 +57,163 @@ import java.util.Map; import java.util.Set; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.AALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.AASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ACONST_NULL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ANEWARRAY; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ARETURN; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ARRAYLENGTH; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ATHROW; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.BALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.BASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.BIPUSH; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.CALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.CASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.CHECKCAST; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.D2F; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.D2I; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.D2L; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DADD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DCMPG; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DCMPL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DCONST_0; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DCONST_1; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DDIV; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DLOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DMUL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DNEG; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DREM; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DRETURN; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DSTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DSUB; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DUP; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DUP2; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DUP2_X1; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DUP2_X2; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DUP_X1; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.DUP_X2; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.F2D; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.F2I; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.F2L; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FADD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FCMPG; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FCMPL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FCONST_0; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FCONST_1; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FCONST_2; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FDIV; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FLOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FMUL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FNEG; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FREM; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FRETURN; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FSTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.FSUB; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.GETFIELD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.GETSTATIC; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.GOTO; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.I2B; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.I2C; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.I2D; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.I2F; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.I2L; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.I2S; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IADD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IAND; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ICONST_0; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ICONST_1; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ICONST_2; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ICONST_3; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ICONST_4; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ICONST_5; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ICONST_M1; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IDIV; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IFEQ; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IFGE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IFGT; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IFLE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IFLT; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IFNE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IFNONNULL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IFNULL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IF_ACMPEQ; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IF_ACMPNE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IF_ICMPEQ; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IF_ICMPGE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IF_ICMPGT; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IF_ICMPLE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IF_ICMPLT; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IF_ICMPNE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IINC; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ILOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IMUL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.INEG; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.INSTANCEOF; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.INVOKEDYNAMIC; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.INVOKEINTERFACE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.INVOKESPECIAL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.INVOKESTATIC; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IOR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IREM; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IRETURN; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ISHL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ISHR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ISTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.ISUB; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IUSHR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.IXOR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.JSR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.L2D; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.L2F; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.L2I; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LADD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LAND; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LCMP; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LCONST_0; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LCONST_1; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LDC; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LDIV; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LLOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LMUL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LNEG; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LOOKUPSWITCH; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LOR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LREM; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LRETURN; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LSHL; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LSHR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LSTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LSUB; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LUSHR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.LXOR; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.MONITORENTER; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.MONITOREXIT; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.MULTIANEWARRAY; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.NEW; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.NEWARRAY; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.NOP; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.POP; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.POP2; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.PUTFIELD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.PUTSTATIC; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.RET; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.RETURN; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.SALOAD; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.SASTORE; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.SIPUSH; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.SWAP; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.TABLESWITCH; +import static org.objectweb.asm.Opcodes.AALOAD; +import static org.objectweb.asm.Opcodes.AASTORE; +import static org.objectweb.asm.Opcodes.ACONST_NULL; +import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ANEWARRAY; +import static org.objectweb.asm.Opcodes.ARETURN; +import static org.objectweb.asm.Opcodes.ARRAYLENGTH; +import static org.objectweb.asm.Opcodes.ASTORE; +import static org.objectweb.asm.Opcodes.ATHROW; +import static org.objectweb.asm.Opcodes.BALOAD; +import static org.objectweb.asm.Opcodes.BASTORE; +import static org.objectweb.asm.Opcodes.BIPUSH; +import static org.objectweb.asm.Opcodes.CALOAD; +import static org.objectweb.asm.Opcodes.CASTORE; +import static org.objectweb.asm.Opcodes.CHECKCAST; +import static org.objectweb.asm.Opcodes.D2F; +import static org.objectweb.asm.Opcodes.D2I; +import static org.objectweb.asm.Opcodes.D2L; +import static org.objectweb.asm.Opcodes.DADD; +import static org.objectweb.asm.Opcodes.DALOAD; +import static org.objectweb.asm.Opcodes.DASTORE; +import static org.objectweb.asm.Opcodes.DCMPG; +import static org.objectweb.asm.Opcodes.DCMPL; +import static org.objectweb.asm.Opcodes.DCONST_0; +import static org.objectweb.asm.Opcodes.DCONST_1; +import static org.objectweb.asm.Opcodes.DDIV; +import static org.objectweb.asm.Opcodes.DLOAD; +import static org.objectweb.asm.Opcodes.DMUL; +import static org.objectweb.asm.Opcodes.DNEG; +import static org.objectweb.asm.Opcodes.DREM; +import static org.objectweb.asm.Opcodes.DRETURN; +import static org.objectweb.asm.Opcodes.DSTORE; +import static org.objectweb.asm.Opcodes.DSUB; +import static org.objectweb.asm.Opcodes.DUP; +import static org.objectweb.asm.Opcodes.DUP2; +import static org.objectweb.asm.Opcodes.DUP2_X1; +import static org.objectweb.asm.Opcodes.DUP2_X2; +import static org.objectweb.asm.Opcodes.DUP_X1; +import static org.objectweb.asm.Opcodes.DUP_X2; +import static org.objectweb.asm.Opcodes.F2D; +import static org.objectweb.asm.Opcodes.F2I; +import static org.objectweb.asm.Opcodes.F2L; +import static org.objectweb.asm.Opcodes.FADD; +import static org.objectweb.asm.Opcodes.FALOAD; +import static org.objectweb.asm.Opcodes.FASTORE; +import static org.objectweb.asm.Opcodes.FCMPG; +import static org.objectweb.asm.Opcodes.FCMPL; +import static org.objectweb.asm.Opcodes.FCONST_0; +import static org.objectweb.asm.Opcodes.FCONST_1; +import static org.objectweb.asm.Opcodes.FCONST_2; +import static org.objectweb.asm.Opcodes.FDIV; +import static org.objectweb.asm.Opcodes.FLOAD; +import static org.objectweb.asm.Opcodes.FMUL; +import static org.objectweb.asm.Opcodes.FNEG; +import static org.objectweb.asm.Opcodes.FREM; +import static org.objectweb.asm.Opcodes.FRETURN; +import static org.objectweb.asm.Opcodes.FSTORE; +import static org.objectweb.asm.Opcodes.FSUB; +import static org.objectweb.asm.Opcodes.GETFIELD; +import static org.objectweb.asm.Opcodes.GETSTATIC; +import static org.objectweb.asm.Opcodes.GOTO; +import static org.objectweb.asm.Opcodes.I2B; +import static org.objectweb.asm.Opcodes.I2C; +import static org.objectweb.asm.Opcodes.I2D; +import static org.objectweb.asm.Opcodes.I2F; +import static org.objectweb.asm.Opcodes.I2L; +import static org.objectweb.asm.Opcodes.I2S; +import static org.objectweb.asm.Opcodes.IADD; +import static org.objectweb.asm.Opcodes.IALOAD; +import static org.objectweb.asm.Opcodes.IAND; +import static org.objectweb.asm.Opcodes.IASTORE; +import static org.objectweb.asm.Opcodes.ICONST_0; +import static org.objectweb.asm.Opcodes.ICONST_1; +import static org.objectweb.asm.Opcodes.ICONST_2; +import static org.objectweb.asm.Opcodes.ICONST_3; +import static org.objectweb.asm.Opcodes.ICONST_4; +import static org.objectweb.asm.Opcodes.ICONST_5; +import static org.objectweb.asm.Opcodes.ICONST_M1; +import static org.objectweb.asm.Opcodes.IDIV; +import static org.objectweb.asm.Opcodes.IFEQ; +import static org.objectweb.asm.Opcodes.IFGE; +import static org.objectweb.asm.Opcodes.IFGT; +import static org.objectweb.asm.Opcodes.IFLE; +import static org.objectweb.asm.Opcodes.IFLT; +import static org.objectweb.asm.Opcodes.IFNE; +import static org.objectweb.asm.Opcodes.IFNONNULL; +import static org.objectweb.asm.Opcodes.IFNULL; +import static org.objectweb.asm.Opcodes.IF_ACMPEQ; +import static org.objectweb.asm.Opcodes.IF_ACMPNE; +import static org.objectweb.asm.Opcodes.IF_ICMPEQ; +import static org.objectweb.asm.Opcodes.IF_ICMPGE; +import static org.objectweb.asm.Opcodes.IF_ICMPGT; +import static org.objectweb.asm.Opcodes.IF_ICMPLE; +import static org.objectweb.asm.Opcodes.IF_ICMPLT; +import static org.objectweb.asm.Opcodes.IF_ICMPNE; +import static org.objectweb.asm.Opcodes.IINC; +import static org.objectweb.asm.Opcodes.ILOAD; +import static org.objectweb.asm.Opcodes.IMUL; +import static org.objectweb.asm.Opcodes.INEG; +import static org.objectweb.asm.Opcodes.INSTANCEOF; +import static org.objectweb.asm.Opcodes.INVOKEDYNAMIC; +import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.IOR; +import static org.objectweb.asm.Opcodes.IREM; +import static org.objectweb.asm.Opcodes.IRETURN; +import static org.objectweb.asm.Opcodes.ISHL; +import static org.objectweb.asm.Opcodes.ISHR; +import static org.objectweb.asm.Opcodes.ISTORE; +import static org.objectweb.asm.Opcodes.ISUB; +import static org.objectweb.asm.Opcodes.IUSHR; +import static org.objectweb.asm.Opcodes.IXOR; +import static org.objectweb.asm.Opcodes.JSR; +import static org.objectweb.asm.Opcodes.L2D; +import static org.objectweb.asm.Opcodes.L2F; +import static org.objectweb.asm.Opcodes.L2I; +import static org.objectweb.asm.Opcodes.LADD; +import static org.objectweb.asm.Opcodes.LALOAD; +import static org.objectweb.asm.Opcodes.LAND; +import static org.objectweb.asm.Opcodes.LASTORE; +import static org.objectweb.asm.Opcodes.LCMP; +import static org.objectweb.asm.Opcodes.LCONST_0; +import static org.objectweb.asm.Opcodes.LCONST_1; +import static org.objectweb.asm.Opcodes.LDC; +import static org.objectweb.asm.Opcodes.LDIV; +import static org.objectweb.asm.Opcodes.LLOAD; +import static org.objectweb.asm.Opcodes.LMUL; +import static org.objectweb.asm.Opcodes.LNEG; +import static org.objectweb.asm.Opcodes.LOOKUPSWITCH; +import static org.objectweb.asm.Opcodes.LOR; +import static org.objectweb.asm.Opcodes.LREM; +import static org.objectweb.asm.Opcodes.LRETURN; +import static org.objectweb.asm.Opcodes.LSHL; +import static org.objectweb.asm.Opcodes.LSHR; +import static org.objectweb.asm.Opcodes.LSTORE; +import static org.objectweb.asm.Opcodes.LSUB; +import static org.objectweb.asm.Opcodes.LUSHR; +import static org.objectweb.asm.Opcodes.LXOR; +import static org.objectweb.asm.Opcodes.MONITORENTER; +import static org.objectweb.asm.Opcodes.MONITOREXIT; +import static org.objectweb.asm.Opcodes.MULTIANEWARRAY; +import static org.objectweb.asm.Opcodes.NEW; +import static org.objectweb.asm.Opcodes.NEWARRAY; +import static org.objectweb.asm.Opcodes.NOP; +import static org.objectweb.asm.Opcodes.POP; +import static org.objectweb.asm.Opcodes.POP2; +import static org.objectweb.asm.Opcodes.PUTFIELD; +import static org.objectweb.asm.Opcodes.PUTSTATIC; +import static org.objectweb.asm.Opcodes.RET; +import static org.objectweb.asm.Opcodes.RETURN; +import static org.objectweb.asm.Opcodes.SALOAD; +import static org.objectweb.asm.Opcodes.SASTORE; +import static org.objectweb.asm.Opcodes.SIPUSH; +import static org.objectweb.asm.Opcodes.SWAP; +import static org.objectweb.asm.Opcodes.TABLESWITCH; // todo this is seriously broken. right now any jump which share the same end and start (i.e. insn 5 jumps into insn 8) will only be run once // however this was based on a flawed assumption that a jump over the same gap would produce the same results every time @@ -221,11 +221,6 @@ public class MethodAnalyzer { private static final boolean DEBUG = false; - @Deprecated - public static AnalyzerResult analyze(WrappedClassNode classNode, MethodNode method) { - return analyze(classNode.classNode, method); - } - public static AnalyzerResult analyze(ClassNode classNode, MethodNode method) { if (Modifier.isAbstract(method.access) || Modifier.isNative(method.access)) { return AnalyzerResult.EMPTY_RESULT; @@ -235,14 +230,28 @@ public static AnalyzerResult analyze(ClassNode classNode, MethodNode method) { List stack = new ArrayList<>(); List locals = new ArrayList<>(); - + + int counter = 0; if (!Modifier.isStatic(method.access)) { - locals.add(new StackObject(new ArgumentFrame(), classNode.name)); + locals.add(new StackObject(new ArgumentFrame(Opcodes.ASTORE, 0), classNode.name)); + counter++; } for (Type type : Type.getArgumentTypes(method.desc)) { Class clazz = PrimitiveUtils.getPrimitiveByName(type.getClassName()); - ArgumentFrame frame = new ArgumentFrame(); + int opcode; + if(clazz == int.class) + opcode = Opcodes.ISTORE; + else if(clazz == long.class) + opcode = Opcodes.LSTORE; + else if(clazz == double.class) + opcode = Opcodes.DSTORE; + else if(clazz == float.class) + opcode = Opcodes.FSTORE; + else + opcode = Opcodes.ASTORE; + ArgumentFrame frame = new ArgumentFrame(opcode, counter); + counter++; if (clazz == null) { locals.add(new StackObject(Object.class, frame, type.getInternalName())); } else { @@ -794,9 +803,9 @@ private static void execute(ClassNode classNode, MethodNode method, AbstractInsn args.add(0, stack.remove(0).value); } currentFrame = new MethodFrame(now.getOpcode(), cast.owner, cast.name, cast.desc, stack.remove(0).value, args); - if (type.getReturnType().getSort() != Type.VOID) { + if (type.getSort() != Type.VOID) { if (clazz == null) { - stack.add(0, new StackObject(Object.class, currentFrame, type.getReturnType().getInternalName())); + stack.add(0, new StackObject(Object.class, currentFrame, type.getInternalName())); } else { stack.add(0, new StackObject(clazz, currentFrame)); } @@ -816,9 +825,9 @@ private static void execute(ClassNode classNode, MethodNode method, AbstractInsn // throw new IllegalArgumentException("Already initialized"); instance.initialize(); currentFrame = new MethodFrame(now.getOpcode(), cast.owner, cast.name, cast.desc, instance.value, args); - if (type.getReturnType().getSort() != Type.VOID) { + if (type.getSort() != Type.VOID) { if (clazz == null) { - stack.add(0, new StackObject(Object.class, currentFrame, type.getReturnType().getInternalName())); + stack.add(0, new StackObject(Object.class, currentFrame, type.getInternalName())); } else { stack.add(0, new StackObject(clazz, currentFrame)); } @@ -834,9 +843,9 @@ private static void execute(ClassNode classNode, MethodNode method, AbstractInsn args.add(0, stack.remove(0).value); } currentFrame = new MethodFrame(now.getOpcode(), cast.owner, cast.name, cast.desc, null, args); - if (type.getReturnType().getSort() != Type.VOID) { + if (type.getSort() != Type.VOID) { if (clazz == null) { - stack.add(0, new StackObject(Object.class, currentFrame, type.getReturnType().getInternalName())); + stack.add(0, new StackObject(Object.class, currentFrame, type.getInternalName())); } else { stack.add(0, new StackObject(clazz, currentFrame)); } @@ -852,9 +861,9 @@ private static void execute(ClassNode classNode, MethodNode method, AbstractInsn args.add(0, stack.remove(0).value); } currentFrame = new MethodFrame(now.getOpcode(), cast.owner, cast.name, cast.desc, stack.remove(0).value, args); - if (type.getReturnType().getSort() != Type.VOID) { + if (type.getSort() != Type.VOID) { if (clazz == null) { - stack.add(0, new StackObject(Object.class, currentFrame, type.getReturnType().getInternalName())); + stack.add(0, new StackObject(Object.class, currentFrame, type.getInternalName())); } else { stack.add(0, new StackObject(clazz, currentFrame)); } @@ -870,9 +879,9 @@ private static void execute(ClassNode classNode, MethodNode method, AbstractInsn args.add(0, stack.remove(0).value); } currentFrame = new MethodFrame(now.getOpcode(), "", cast.name, cast.desc, null, args); - if (type.getReturnType().getSort() != Type.VOID) { + if (type.getSort() != Type.VOID) { if (clazz == null) { - stack.add(0, new StackObject(Object.class, currentFrame, type.getReturnType().getInternalName())); + stack.add(0, new StackObject(Object.class, currentFrame, type.getInternalName())); } else { stack.add(0, new StackObject(clazz, currentFrame)); } @@ -899,7 +908,15 @@ private static void execute(ClassNode classNode, MethodNode method, AbstractInsn TypeInsnNode cast = (TypeInsnNode) now; currentFrame = new NewArrayFrame(now.getOpcode(), cast.desc, len); String desc = null; - Type type = Type.getType(cast.desc); + Type type; + try { + // if array is multidimensional, type is a descriptor + type = Type.getType(cast.desc); + } catch (Throwable ignored) { + // however, if array is single dimensional, type is an object type + // this is especially bad if the object type is something like "LongHashMap" because it starts with an "L" + type = Type.getObjectType(cast.desc); + } if (type.getSort() == Type.ARRAY) { desc = type.getDescriptor(); } else { @@ -968,6 +985,7 @@ private static void execute(ClassNode classNode, MethodNode method, AbstractInsn if (currentFrame != null) { List thisFrame = result.frames.computeIfAbsent(now, k -> new ArrayList<>()); thisFrame.add(currentFrame); + result.mapping = null; result.maxLocals = Math.max(result.maxLocals, locals.size()); result.maxStack = Math.max(result.maxStack, stack.size()); @@ -998,7 +1016,7 @@ private static void execute(ClassNode classNode, MethodNode method, AbstractInsn for (TryCatchBlockNode tcbn : handler) { if (jumped.add(new AbstractMap.SimpleEntry<>(now, tcbn.handler))) { List newStack = new ArrayList<>(); - newStack.add(0, new StackObject(new ArgumentFrame(), tcbn.type == null ? "java/lang/Throwable" : tcbn.type)); + newStack.add(0, new StackObject(new ArgumentFrame(-1, -1), tcbn.type == null ? "java/lang/Throwable" : tcbn.type)); List newLocals = new ArrayList<>(); newLocals.addAll(locals); execute(classNode, method, tcbn.handler, newStack, newLocals, handlers, result, jumped); diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/ValueType.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/ValueType.java index 74084eb6..98b87e90 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/ValueType.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/ValueType.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; +import org.objectweb.asm.Type; public enum ValueType { TOP(null), diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ArgumentFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ArgumentFrame.java index c046ccbb..20d3138f 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ArgumentFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ArgumentFrame.java @@ -17,8 +17,21 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; public class ArgumentFrame extends Frame { - public ArgumentFrame() { + private int opcode; + private int local; + + public ArgumentFrame(int opcode, int local) { super(-1); + this.opcode = opcode; + this.local = local; + } + + public int getLocal() { + return local; + } + + public int getStoreOpcode() { + return opcode; } // fixme how to determine? diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ArrayLengthFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ArrayLengthFrame.java index 1399045a..6ac9ff5b 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ArrayLengthFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ArrayLengthFrame.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; +import org.objectweb.asm.Opcodes; public class ArrayLengthFrame extends Frame { private Frame array; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/CheckCastFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/CheckCastFrame.java index 76ed005d..1ed20268 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/CheckCastFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/CheckCastFrame.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; +import org.objectweb.asm.Opcodes; public class CheckCastFrame extends Frame { private Frame check; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/InstanceofFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/InstanceofFrame.java index 3442c7ef..210c230e 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/InstanceofFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/InstanceofFrame.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; +import org.objectweb.asm.Opcodes; public class InstanceofFrame extends Frame { private Frame check; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/JumpFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/JumpFrame.java index affa1655..78b50512 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/JumpFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/JumpFrame.java @@ -16,8 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.AbstractInsnNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.AbstractInsnNode; import java.util.ArrayList; import java.util.Arrays; @@ -25,7 +24,7 @@ public class JumpFrame extends Frame { private List comparators = new ArrayList<>(); - private transient List targets; + private transient List targets; public JumpFrame(int opcode, List comparators, AbstractInsnNode... targets) { super(opcode); diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/LocalFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/LocalFrame.java index 99c50f32..85269d29 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/LocalFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/LocalFrame.java @@ -11,4 +11,12 @@ public LocalFrame(int opcode, int local, Frame value) { if (this.value != null) this.value.children.add(this); } + + public int getLocal() { + return local; + } + + public Frame getValue() { + return value; + } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MathFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MathFrame.java index e0a60044..58a6e400 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MathFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MathFrame.java @@ -1,7 +1,6 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MethodFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MethodFrame.java index a299c4ee..b48e93c2 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MethodFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MethodFrame.java @@ -46,6 +46,18 @@ public List getArgs() { return Collections.unmodifiableList(args); } + public String getDesc() { + return desc; + } + + public String getName() { + return name; + } + + public String getOwner() { + return owner; + } + public Frame getInstance() { return instance; } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MultiANewArrayFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MultiANewArrayFrame.java index d3f4c80e..112a6d2f 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MultiANewArrayFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/MultiANewArrayFrame.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; +import org.objectweb.asm.Opcodes; import java.util.List; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/NewFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/NewFrame.java index dd421e32..6032b69f 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/NewFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/NewFrame.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; +import org.objectweb.asm.Opcodes; public class NewFrame extends Frame { private String ntype; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/PopFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/PopFrame.java index 2725727a..cbbcc67a 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/PopFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/PopFrame.java @@ -1,6 +1,5 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/SwapFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/SwapFrame.java index 33e2f0ed..80c0284a 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/SwapFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/SwapFrame.java @@ -1,6 +1,6 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; +import org.objectweb.asm.Opcodes; public class SwapFrame extends Frame { private Frame top; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/SwitchFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/SwitchFrame.java index 1bb433c8..cdfd7188 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/SwitchFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/SwitchFrame.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LabelNode; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ThrowFrame.java b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ThrowFrame.java index b7169477..4d6c4c3f 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ThrowFrame.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/analyzer/frame/ThrowFrame.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.analyzer.frame; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; +import org.objectweb.asm.Opcodes; public class ThrowFrame extends Frame { diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/asm/ConstantPool.java b/src/main/java/com/javadeobfuscator/deobfuscator/asm/ConstantPool.java new file mode 100644 index 00000000..5e363305 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/asm/ConstantPool.java @@ -0,0 +1,32 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.asm; + +import org.objectweb.asm.ClassReader; + +public class ConstantPool { + private final ClassReader classReader; + + // todo load constants lazily (to prevent abuse with large constant pools) + public ConstantPool(ClassReader classReader) { + this.classReader = classReader; + } + + public int getSize() { + return this.classReader.getItemCount(); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/ConstantPropagatingSourceFinder.java b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/ConstantPropagatingSourceFinder.java new file mode 100644 index 00000000..00429741 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/ConstantPropagatingSourceFinder.java @@ -0,0 +1,149 @@ +/* + * Copyright 2018 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.asm.source; + +import org.objectweb.asm.tree.*; +import org.objectweb.asm.tree.analysis.*; + +import java.util.*; + +public class ConstantPropagatingSourceFinder extends SourceFinderConsumer { + public ConstantPropagatingSourceFinder(SourceFinderConsumer parent) { + super(parent); + } + + public ConstantPropagatingSourceFinder() { + super(null); + } + + @Override + public SourceResult findSource(MethodNode methodNode, Frame[] frames, List instructions, AbstractInsnNode source, SourceValue want, AbstractInsnNode now) { + Frame sourceFrame = frames[methodNode.instructions.indexOf(source)]; + Frame curFrame = frames[methodNode.instructions.indexOf(now)]; + + switch (now.getOpcode()) { + case IADD: + case ISUB: + case IMUL: + case IDIV: + case IREM: + case IAND: + case IOR: + case IXOR: + case ISHL: + case ISHR: + case IUSHR: { + instructions.add(now); + SourceResult bres = SourceFinder.findSource(methodNode, frames, instructions, this, now, curFrame.getStack(curFrame.getStackSize() - 2)); + SourceResult ares = SourceFinder.findSource(methodNode, frames, instructions, this, now, curFrame.getStack(curFrame.getStackSize() - 1)); + if (bres.isUnknown() || ares.isUnknown()) return SourceResult.unknown(); + if (!bres.getExceptions().isEmpty() || !ares.getExceptions().isEmpty()) + return SourceResult.unknown(); // todo maybe we should merge this? + if (bres.getValues().size() != ares.getValues().size()) + throw new RuntimeException("unexpected different size of values " + bres + " " + ares); + +// System.out.println("got " + TransformerHelper.insnToString(now) + " " + bres + " " + ares); + List values = new ArrayList<>(); + List exceptions = new ArrayList<>(); + for (int i = 0; i < ares.getValues().size(); i++) { + int b = (int) bres.getValues().get(i); + int a = (int) ares.getValues().get(i); +// System.out.println("got " + TransformerHelper.insnToString(now) + " " + b + " " + a); + switch (now.getOpcode()) { + case IADD: { + values.add(b + a); + break; + } + case ISUB: { + values.add(b - a); + break; + } + case IMUL: { + values.add(b * a); + break; + } + case IDIV: { + if (a == 0) exceptions.add(new ExceptionHolder("java/lang/ArithmeticException")); + else values.add(b / a); + break; + } + case IREM: { + if (a == 0) exceptions.add(new ExceptionHolder("java/lang/ArithmeticException")); + else values.add(b % a); + break; + } + case IAND: { + values.add(b & a); + break; + } + case IOR: { + values.add(b | a); + break; + } + case IXOR: { + values.add(b ^ a); + break; + } + case ISHL: { + values.add(b << a); + break; + } + case ISHR: { + values.add(b >> a); + break; + } + case IUSHR: { + values.add(b >>> a); + break; + } + default: { + throw new RuntimeException(); + } + } + } + return new SourceResult(values, exceptions); + } + case DUP2: + case SWAP: { + instructions.add(now); + if (getStackOffset(sourceFrame, want) == sourceFrame.getStackSize() - 1) { + return SourceFinder.findSource(methodNode, frames, instructions, this, now, curFrame.getStack(curFrame.getStackSize() - 2)); + } else if (getStackOffset(sourceFrame, want) == sourceFrame.getStackSize() - 2) { + return SourceFinder.findSource(methodNode, frames, instructions, this, now, curFrame.getStack(curFrame.getStackSize() - 1)); + } + throw new RuntimeException(String.valueOf(getStackOffset(sourceFrame, want))); + } + case DUP: + case DUP_X1: { + instructions.add(now); + return SourceFinder.findSource(methodNode, frames, instructions, this, now, curFrame.getStack(curFrame.getStackSize() - 1)); + } + } + + return parent == null ? SourceResult.unknown() : parent.findSource(methodNode, frames, instructions, source, want, now); + } + + private static int getStackOffset(Frame frame, SourceValue want) { + for (int i = 0; i < frame.getStackSize(); i++) { + if (frame.getStack(i) == want) { + return i; + } + } + + return -1; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/ExceptionHolder.java b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/ExceptionHolder.java new file mode 100644 index 00000000..43cce522 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/ExceptionHolder.java @@ -0,0 +1,69 @@ +/* + * Copyright 2018 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.asm.source; + +import java.util.*; + +/** + * Since {@link java.lang.Throwable} doesn't override {@link Object#equals(Object)}, we use an ExceptionHolder to represent + * arbitrary exceptions + */ +public class ExceptionHolder { + private final String exceptionType; + private final Map exceptionProperties; + + public ExceptionHolder(String exceptionType) { + this(exceptionType, Collections.emptyMap()); + } + + public ExceptionHolder(String exceptionType, Map exceptionProperties) { + this.exceptionType = exceptionType; + this.exceptionProperties = new HashMap<>(); + this.exceptionProperties.putAll(exceptionProperties); + } + + public String getExceptionType() { + return exceptionType; + } + + public Map getExceptionProperties() { + return exceptionProperties; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ExceptionHolder that = (ExceptionHolder) o; + return Objects.equals(exceptionType, that.exceptionType) && + Objects.equals(exceptionProperties, that.exceptionProperties); + } + + @Override + public int hashCode() { + + return Objects.hash(exceptionType, exceptionProperties); + } + + @Override + public String toString() { + return "ExceptionHolder{" + + "exceptionType='" + exceptionType + '\'' + + ", exceptionProperties=" + exceptionProperties + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SimpleMethodConstantSourceFinder.java b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SimpleMethodConstantSourceFinder.java new file mode 100644 index 00000000..938cc4cb --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SimpleMethodConstantSourceFinder.java @@ -0,0 +1,39 @@ +/* + * Copyright 2018 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.asm.source; + +import java.util.List; + +import org.objectweb.asm.tree.*; +import org.objectweb.asm.tree.analysis.*; + +public class SimpleMethodConstantSourceFinder extends SourceFinderConsumer { + public SimpleMethodConstantSourceFinder(SourceFinderConsumer parent) { + super(parent); + } + + public SimpleMethodConstantSourceFinder() { + super(null); + } + + @Override + public SourceResult findSource(MethodNode methodNode, Frame[] frames, List instructions, AbstractInsnNode source, SourceValue want, AbstractInsnNode now) { + + + return parent == null ? SourceResult.unknown() : parent.findSource(methodNode, frames, instructions, source, want, now); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceFinder.java b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceFinder.java new file mode 100644 index 00000000..544529e9 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceFinder.java @@ -0,0 +1,94 @@ +/* + * Copyright 2018 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.asm.source; + +import org.objectweb.asm.*; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.tree.analysis.*; + +import java.util.*; + +public class SourceFinder implements Opcodes { + + public static SourceResult findSource(MethodNode methodNode, Frame[] frames, List instructions, SourceFinderConsumer sfc, AbstractInsnNode now, SourceValue want) { + // Remember, Frame stores the stack/locals *before* the instruction is executed + + List foundResults = new ArrayList<>(); + List foundExceptions = new ArrayList<>(); + + for (AbstractInsnNode sourceInsn : want.insns) { + instructions.add(sourceInsn); +// System.out.println(TransformerHelper.insnToString(now) + " -> " + TransformerHelper.insnToString(sourceInsn)); + switch (sourceInsn.getOpcode()) { + // Explicit push + case ACONST_NULL: { + foundResults.add(null); + break; + } + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: { + foundResults.add(sourceInsn.getOpcode() - ICONST_0); + break; + } + case LCONST_0: + case LCONST_1: { + foundResults.add((long) (sourceInsn.getOpcode() - LCONST_0)); + break; + } + case FCONST_0: + case FCONST_1: + case FCONST_2: { + foundResults.add((float) (sourceInsn.getOpcode() - FCONST_0)); + break; + } + case DCONST_0: + case DCONST_1: { + foundResults.add((double) (sourceInsn.getOpcode() - DCONST_0)); + break; + } + case BIPUSH: + case SIPUSH: { + foundResults.add(((IntInsnNode) sourceInsn).operand); + break; + } + case LDC: { + foundResults.add(((LdcInsnNode) sourceInsn).cst); + break; + } + default: { + if (sfc == null) { + return SourceResult.unknown(); + } + SourceResult found = sfc.findSource(methodNode, frames, instructions, now, want, sourceInsn); + if (found.isUnknown()) { + return SourceResult.unknown(); + } + foundResults.addAll(found.getValues()); + foundExceptions.addAll(found.getExceptions()); + break; + } + } + } + + return new SourceResult(foundResults, foundExceptions); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceFinderConsumer.java b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceFinderConsumer.java new file mode 100644 index 00000000..6b32e709 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceFinderConsumer.java @@ -0,0 +1,33 @@ +/* + * Copyright 2018 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.asm.source; + +import java.util.List; + +import org.objectweb.asm.*; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.tree.analysis.*; + +public abstract class SourceFinderConsumer implements Opcodes { + protected final SourceFinderConsumer parent; + + public SourceFinderConsumer(SourceFinderConsumer parent) { + this.parent = parent; + } + + public abstract SourceResult findSource(MethodNode methodNode, Frame[] frames, List instructions, AbstractInsnNode source, SourceValue want, AbstractInsnNode now); +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceResult.java b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceResult.java new file mode 100644 index 00000000..bd2060db --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/asm/source/SourceResult.java @@ -0,0 +1,92 @@ +/* + * Copyright 2018 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.asm.source; + +import java.util.*; + +public class SourceResult { + + private final List values = new ArrayList<>(); + private final Set valuesDeduped = new HashSet<>(); + private final List exceptions = new ArrayList<>(); + private final Set exceptionsDeduped = new HashSet<>(); + + public SourceResult(List values, List exceptions) { + this.values.addAll(values); + this.exceptions.addAll(exceptions); + this.valuesDeduped.addAll(values); + this.exceptionsDeduped.addAll(exceptions); + } + + public static SourceResult unknown() { + return new SourceResult(Collections.emptyList(), Collections.emptyList()); + } + + public static SourceResult values(List values) { + return new SourceResult(values, Collections.emptyList()); + } + + public static SourceResult exceptions(List exceptions) { + return new SourceResult(Collections.emptyList(), exceptions); + } + + public static SourceResult values(Object... values) { + return new SourceResult(Arrays.asList(values), Collections.emptyList()); + } + + public static SourceResult exceptions(ExceptionHolder... exceptions) { + return new SourceResult(Collections.emptyList(), Arrays.asList(exceptions)); + } + + public Set getValuesDeduped() { + return valuesDeduped; + } + + public Optional consensus() { + return valuesDeduped.size() == 1 ? + Optional.of(valuesDeduped.iterator().next()) : Optional.empty(); + } + + public OptionalInt intConsensus() { + return valuesDeduped.size() == 1 && valuesDeduped.iterator().next() instanceof Integer ? + OptionalInt.of((Integer) valuesDeduped.iterator().next()) : OptionalInt.empty(); + } + + public Set getExceptionsDeduped() { + return exceptionsDeduped; + } + + public boolean isUnknown() { + return values.size() == 0 && exceptions.size() == 0; + } + + @Override + public String toString() { + return "SourceResult{" + + "values=" + values + + ", exceptions=" + exceptions + + '}'; + } + + public List getValues() { + return values; + } + + public List getExceptions() { + return exceptions; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/config/Configuration.java b/src/main/java/com/javadeobfuscator/deobfuscator/config/Configuration.java new file mode 100644 index 00000000..ae341f50 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/config/Configuration.java @@ -0,0 +1,192 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.config; + +import com.fasterxml.jackson.annotation.*; + +import java.io.*; +import java.util.*; + +public class Configuration { + + @JsonProperty + private File input; + + @JsonProperty + private File output; + + @JsonProperty + private List transformers; + + @JsonProperty + private List path; + + @JsonProperty + private List libraries; + + @JsonProperty + private List ignoredClasses; + + @JsonProperty + private boolean smartRedo; + + @JsonProperty + private boolean verify; + + @JsonProperty + private boolean detect; + + /** + * Allows patching of ASM-crashing exploits. + */ + @JsonProperty + private boolean patchAsm; + + /** + * Must enable for paramorphism obfuscated files. + */ + @JsonProperty + private boolean paramorphism; + + /** + * Must enable for paramorphism v2 obfuscated files. + */ + @JsonProperty + private boolean paramorphismV2; + + @JsonProperty + private boolean debugRulesAnalyzer; + + /** + * Some obfuscators like to have junk classes. If ALL your libraries are added, enable this to dump troublesome classes. Note that this will not get rid of all + * junk classes. + */ + @JsonProperty + private boolean deleteUselessClasses; + + public File getInput() { + return input; + } + + public void setInput(File input) { + this.input = input; + } + + public File getOutput() { + return output; + } + + public void setOutput(File output) { + this.output = output; + } + + public List getTransformers() { + return transformers; + } + + public void setTransformers(List transformers) { + this.transformers = transformers; + } + + public List getPath() { + return path; + } + + public void setPath(List path) { + this.path = path; + } + + public boolean isVerify() { + return verify; + } + + public void setVerify(boolean verify) { + this.verify = verify; + } + + public List getIgnoredClasses() { + return ignoredClasses; + } + + public void setIgnoredClasses(List ignoredClasses) { + this.ignoredClasses = ignoredClasses; + } + + public List getLibraries() { + return libraries; + } + + public void setLibraries(List libraries) { + this.libraries = libraries; + } + + public boolean isDetect() { + return detect; + } + + public void setDetect(boolean detect) { + this.detect = detect; + } + + public boolean isPatchAsm() { + return patchAsm; + } + + public void setPatchAsm(boolean patchAsm) { + this.patchAsm = patchAsm; + } + + public boolean isSmartRedo() { + return smartRedo; + } + + public void setSmartRedo(boolean smartRedo) { + this.smartRedo = smartRedo; + } + + public boolean isParamorphism() { + return paramorphism; + } + + public void setParamorphism(boolean paramorphism) { + this.paramorphism = paramorphism; + } + + public boolean isParamorphismV2() { + return paramorphismV2; + } + + public void setParamorphismV2(boolean paramorphismV2) { + this.paramorphismV2 = paramorphismV2; + } + + public boolean isDebugRulesAnalyzer() { + return debugRulesAnalyzer; + } + + public void setDebugRulesAnalyzer(boolean debugRulesAnalyzer) { + this.debugRulesAnalyzer = debugRulesAnalyzer; + } + + public boolean isDeleteUselessClasses() { + return deleteUselessClasses; + } + + public void setDeleteUselessClasses(boolean deleteUselessClasses) { + this.deleteUselessClasses = deleteUselessClasses; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/config/TransformerConfig.java b/src/main/java/com/javadeobfuscator/deobfuscator/config/TransformerConfig.java new file mode 100644 index 00000000..48c2a36e --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/config/TransformerConfig.java @@ -0,0 +1,80 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.config; + +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import com.javadeobfuscator.javavm.*; + +import java.lang.annotation.*; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.function.*; + +@SuppressWarnings("rawtypes") +public class TransformerConfig { + @Inherited + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface ConfigOptions { + Class configClass(); + } + + private final Class implementation; + + private List> vmModifiers = new ArrayList<>(); + + public TransformerConfig(Class implementation) { + this.implementation = implementation; + } + + public Class getImplementation() { + return implementation; + } + + public List> getVmModifiers() { + return vmModifiers; + } + + public void setVmModifiers(List> vmModifiers) { + this.vmModifiers = vmModifiers; + } + + public static TransformerConfig configFor(Class implementation) { + TransformerConfig.ConfigOptions options = implementation.getAnnotation(TransformerConfig.ConfigOptions.class); + if (options != null) { + Class configClass = options.configClass(); + +// try { +// return configClass.getConstructor(Class.class).newInstance(implementation); +// } catch (NoSuchMethodException ignored) { +// } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { +// throw new RuntimeException("Error instantiating TransformerConfig for " + implementation.getCanonicalName(), e); +// } + + try { + return configClass.getConstructor().newInstance(); + } catch (NoSuchMethodException ignored) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Error instantiating TransformerConfig for " + implementation.getCanonicalName(), e); + } + + throw new RuntimeException("Could not find suitable constructor for TransformerConfig"); + } else { + return new TransformerConfig(implementation); + } + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/config/TransformerConfigDeserializer.java b/src/main/java/com/javadeobfuscator/deobfuscator/config/TransformerConfigDeserializer.java new file mode 100644 index 00000000..f8adb18a --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/config/TransformerConfigDeserializer.java @@ -0,0 +1,102 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.config; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.*; +import com.javadeobfuscator.deobfuscator.transformers.DelegatingTransformer; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import org.slf4j.Logger; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class TransformerConfigDeserializer extends JsonDeserializer { + + private Logger logger; + + public TransformerConfigDeserializer(Logger logger) { + this.logger = logger; + } + + @Override + public TransformerConfig deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + JsonNode node = p.getCodec().readTree(p); + + if (node.isObject()) { + List configs = new ArrayList<>(); + + for (Iterator> it = node.fields(); it.hasNext(); ) { + Map.Entry field = it.next(); + + TransformerConfig config = getById(p, ctxt, field.getKey()); + + // todo is there a better way to do this other than making a new ObjectMapper? + ObjectMapper mapper = new ObjectMapper(new JsonFactory()); + configs.add(mapper.readerForUpdating(config).readValue(field.getValue())); + } + + if (configs.size() == 0) { + throw new IllegalArgumentException("didn't see that coming " + node); + } else if (configs.size() == 1) { + return configs.get(0); + } else { + DelegatingTransformer.Config config = new DelegatingTransformer.Config(); + config.setConfigs(configs); + return config; + } + } else if (node.isTextual()) { + return getById(p, ctxt, node.asText()); + } else { + throw ctxt.wrongTokenException(p, String.class, JsonToken.VALUE_STRING, null); + } + } + + private TransformerConfig getById(JsonParser p, DeserializationContext ctxt, String id) throws JsonMappingException { + Class clazz = null; + try { + clazz = Class.forName("com.javadeobfuscator.deobfuscator.transformers." + id); + } catch (ClassNotFoundException ignored) { + } + if (clazz == null) { + try { + clazz = Class.forName(id); + } catch (ClassNotFoundException ignored) { + } + } + + if (clazz == DelegatingTransformer.class) { + throw ctxt.weirdStringException(id, Transformer.class, "Cannot explicitly request DelegatingTransformer"); + } + + if (clazz != null) { + if (Transformer.class.isAssignableFrom(clazz)) { + return TransformerConfig.configFor(clazz.asSubclass(Transformer.class)); + } else { + throw ctxt.weirdStringException(id, Transformer.class, "Class does not extend com.javadeobfuscator.deobfuscator.transformers.Transformer"); + } + } else { + throw ctxt.weirdStringException(id, Transformer.class, "Could not locate specified transformer"); + } + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/exceptions/WrongTransformerException.java b/src/main/java/com/javadeobfuscator/deobfuscator/exceptions/WrongTransformerException.java new file mode 100644 index 00000000..1dabf717 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/exceptions/WrongTransformerException.java @@ -0,0 +1,38 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.exceptions; + +public class WrongTransformerException extends RuntimeException { + public WrongTransformerException() { + } + + public WrongTransformerException(String message) { + super(message); + } + + public WrongTransformerException(String message, Throwable cause) { + super(message, cause); + } + + public WrongTransformerException(Throwable cause) { + super(cause); + } + + public WrongTransformerException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/Context.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/Context.java index 5db58c01..2160faa1 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/Context.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/Context.java @@ -1,21 +1,27 @@ package com.javadeobfuscator.deobfuscator.executor; +import com.javadeobfuscator.deobfuscator.asm.ConstantPool; import com.javadeobfuscator.deobfuscator.executor.providers.Provider; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.AbstractInsnNode; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; import java.io.File; import java.util.*; +import java.util.function.BiFunction; import java.util.function.Consumer; public class Context { //FIXME clinit classes - private List context = new ArrayList<>(); + private final List stackTrace = new ArrayList<>(); public Provider provider; - public Map dictionary; + public Map dictionary; + public Map constantPools; + public Map, Context, JavaValue>> customMethodFunc = new HashMap<>(); + public ThreadStore threadStore = new ThreadStore(); + public Monitor monitor = new Monitor(); - public Set clinit = new HashSet<>(); + public Set clinit = Collections.synchronizedSet(new HashSet<>()); public File file; @@ -23,32 +29,55 @@ public Context(Provider provider) { this.provider = provider; } + public Context copyForNewThread() { + Context threadContext = new Context(provider); + threadContext.dictionary = dictionary; + threadContext.constantPools = constantPools; + threadContext.customMethodFunc = customMethodFunc; + threadContext.threadStore = threadStore; + threadContext.monitor = monitor; + threadContext.clinit = clinit; + threadContext.file = file; + threadContext.breakpointsBefore = breakpointsBefore; + threadContext.breakpointsAfter = breakpointsAfter; + return threadContext; + } + public StackTraceElement at(int index) { - return context.get(index); + return stackTrace.get(index); } public StackTraceElement pop() { - return context.remove(0); + return stackTrace.remove(0); } public void push(String clazz, String method, int constantPoolSize) { clazz = clazz.replace('/', '.'); - context.add(0, new StackTraceElement(clazz, method, "", constantPoolSize)); + stackTrace.add(0, new StackTraceElement(clazz, method, "", constantPoolSize)); + } + + public void push(String clazz, String method, String sourceFile, int constantPoolSize) { + clazz = clazz.replace('/', '.'); + stackTrace.add(0, new StackTraceElement(clazz, method, sourceFile, constantPoolSize)); } public int size() { - return context.size(); + return stackTrace.size(); } public StackTraceElement[] getStackTrace() { StackTraceElement[] orig = new StackTraceElement[size()]; for (int i = 0; i < size(); i++) { StackTraceElement e = at(i); - orig[i] = new StackTraceElement(e.getClassName(), e.getMethodName(), null, -1); + orig[i] = new StackTraceElement(e.getClassName(), e.getMethodName(), e.getFileName(), -1); } return orig; } + public void clearStackTrace() { + stackTrace.clear(); + } + private Map> breakpointsBefore = new HashMap<>(); private Map> breakpointsAfter = new HashMap<>(); @@ -68,9 +97,9 @@ public void breakpoint(AbstractInsnNode now, Consumer before, Co } public static class BreakpointInfo { - private Object throwable; - private List stack; - private List locals; + private final Object throwable; + private final List stack; + private final List locals; public BreakpointInfo(Object throwable, List stack, List locals) { this.throwable = throwable; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/MethodExecutor.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/MethodExecutor.java index cf95ffe5..8edc3efc 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/MethodExecutor.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/MethodExecutor.java @@ -16,33 +16,24 @@ package com.javadeobfuscator.deobfuscator.executor; -import static com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes.*; +import static org.objectweb.asm.Opcodes.*; import java.lang.reflect.Array; import java.lang.reflect.Modifier; import java.util.*; +import java.util.Map.Entry; import java.util.function.BiFunction; import com.google.common.base.Optional; import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaClass; import com.javadeobfuscator.deobfuscator.executor.exceptions.*; -import com.javadeobfuscator.deobfuscator.executor.values.JavaAddress; -import com.javadeobfuscator.deobfuscator.executor.values.JavaBoolean; -import com.javadeobfuscator.deobfuscator.executor.values.JavaByte; -import com.javadeobfuscator.deobfuscator.executor.values.JavaCharacter; -import com.javadeobfuscator.deobfuscator.executor.values.JavaDouble; -import com.javadeobfuscator.deobfuscator.executor.values.JavaFloat; -import com.javadeobfuscator.deobfuscator.executor.values.JavaInteger; -import com.javadeobfuscator.deobfuscator.executor.values.JavaLong; -import com.javadeobfuscator.deobfuscator.executor.values.JavaObject; -import com.javadeobfuscator.deobfuscator.executor.values.JavaShort; -import com.javadeobfuscator.deobfuscator.executor.values.JavaTop; -import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.*; +import com.javadeobfuscator.deobfuscator.executor.values.*; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; import com.javadeobfuscator.deobfuscator.utils.PrimitiveUtils; +import com.javadeobfuscator.deobfuscator.utils.TypeStore; import com.javadeobfuscator.deobfuscator.utils.Utils; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; public class MethodExecutor { private static final boolean VERIFY; @@ -59,17 +50,17 @@ public class MethodExecutor { DEBUG_METHODS_WITH_DESC = Arrays.asList(); } - public static T execute(WrappedClassNode classNode, MethodNode method, List args, Object instance, Context context) { + public static T execute(ClassNode classNode, MethodNode method, List args, Object instance, Context context) { if (context == null) throw new IllegalArgumentException("Null context"); List stack = new LinkedList<>(); List locals = new LinkedList<>(); if (!Modifier.isStatic(method.access)) { - if (instance != null && instance instanceof JavaObject) { - locals.add((JavaObject) instance); - } else { - locals.add(new JavaObject(instance, "java/lang/Object")); - } + if (instance == null) { + locals.add(new JavaObject(null, "java/lang/Object")); + } else { + locals.add((JavaValue)instance); + } } if (args != null) { for (JavaValue arg : args) { @@ -96,7 +87,8 @@ private static void executeArrayLoad(List stack, Class type) { throw new ExecutionException("Expected Object on stack"); } int index = stack.remove(0).intValue(); - Object array = stack.remove(0).value(); + JavaValue arrValue = stack.remove(0); + Object array = arrValue.value(); if (VERIFY && array == null) { throw new ExecutionException("Array is null"); } @@ -133,7 +125,10 @@ else if (value instanceof Boolean) result = new JavaShort((Short) value); break; default: - result = new JavaObject(value, "java/lang/Object"); + if(value != null && value.getClass().isArray()) + result = new JavaArray(value); + else + result = new JavaObject(value, ((JavaArray)arrValue).getValueType(index)); break; } stack.add(0, result); @@ -180,12 +175,30 @@ else if(value instanceof JavaLong) Array.set(array.value(), index.intValue(), (char)(value.longValue())); else Array.set(array.value(), index.intValue(), val); + } else if (array.value() instanceof byte[]) { + //We have to unbox everything or it throws an exception + if(value instanceof JavaShort) + Array.set(array.value(), index.intValue(), (byte)((JavaShort)value).shortValue()); + else if(value instanceof JavaInteger) + Array.set(array.value(), index.intValue(), (byte)value.intValue()); + else + Array.set(array.value(), index.intValue(), val); + } else if (array.value() instanceof short[]) { + //We have to unbox everything or it throws an exception + if(value instanceof JavaByte) + Array.set(array.value(), index.intValue(), (short)((JavaByte)value).byteValue()); + else if(value instanceof JavaInteger) + Array.set(array.value(), index.intValue(), (short)value.intValue()); + else + Array.set(array.value(), index.intValue(), val); } else - Array.set(array.value(), index.intValue(), val); + Array.set(array.value(), index.intValue(), val); + if(value instanceof JavaObject) + ((JavaArray)array).onValueStored(index.intValue(), value.type()); } public static Object convert(Object value, String type) { - if (value instanceof Number && Utils.isDigit(type)) { + if (value instanceof Number && Utils.canReturnDigit(type)) { Number cast = (Number) value; switch (type) { case "I": @@ -196,11 +209,23 @@ public static Object convert(Object value, String type) { return cast.byteValue(); case "J": return cast.longValue(); + case "Z": + return cast.intValue() != 0; + case "C": + return (char)cast.intValue(); } } return value; } + + public static void convertArgs(List args, Type[] methodParams) { + for(int i = 0; i < args.size(); i++) { + JavaValue arg = args.get(i); + if(arg instanceof JavaInteger && methodParams[i].getDescriptor().equals("I")) + args.set(i, new JavaInteger(arg.intValue())); + } + } public static Object value(JavaValue value) { Object val; @@ -218,6 +243,9 @@ public static Object value(JavaValue value) { case "JavaInteger": val = value.intValue(); break; + case "JavaBoolean": + val = value.booleanValue(); + break; case "JavaLong": val = value.longValue(); break; @@ -228,6 +256,7 @@ public static Object value(JavaValue value) { val = value.doubleValue(); break; case "JavaObject": + case "JavaArray": val = value.value(); break; default: @@ -326,15 +355,15 @@ private static void doFloatMathReturnInteger(List stack, BiFunction T execute(WrappedClassNode classNode, MethodNode method, AbstractInsnNode now, List stack, List locals, Context context) { - context.push(classNode.classNode.name, method.name, classNode.constantPoolSize); + private static T execute(ClassNode classNode, MethodNode method, AbstractInsnNode now, List stack, List locals, Context context) { + context.push(classNode.name, method.name, classNode.sourceFile, 0); // constantPoolSize isn't even used, is it? if (DEBUG) { - System.out.println("Executing " + classNode.classNode.name + " " + method.name + method.desc); + System.out.println("Executing " + classNode.name + " " + method.name + method.desc); } forever: while (true) { try { - if (DEBUG && (DEBUG_CLASSES.isEmpty() || DEBUG_CLASSES.contains(classNode.classNode.name)) && (DEBUG_METHODS_WITH_DESC.isEmpty() || DEBUG_METHODS_WITH_DESC.contains(method.name + method.desc))) { + if (DEBUG && (DEBUG_CLASSES.isEmpty() || DEBUG_CLASSES.contains(classNode.name)) && (DEBUG_METHODS_WITH_DESC.isEmpty() || DEBUG_METHODS_WITH_DESC.contains(method.name + method.desc))) { System.out.println("\t Stack: " + stack); System.out.println("\t Locals: " + locals); System.out.println(); @@ -506,7 +535,7 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst case CASTORE: case SASTORE: case AASTORE: - executeArrayStore(stack); + executeArrayStore(stack); break; case POP: stack.remove(0); @@ -945,7 +974,7 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst } case IRETURN: { context.pop(); - return (T) (Integer) stack.remove(0).intValue(); + return (T) convert(value(stack.remove(0)), Type.getReturnType(method.desc).getDescriptor()); } case LRETURN: { context.pop(); @@ -963,7 +992,9 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst } case ARETURN: { context.pop(); - return (T) stack.remove(0).value(); + JavaValue value = stack.remove(0); + TypeStore.returnObjects.put(Thread.currentThread().getId(), (JavaObject)value); + return (T) value.value(); } case RETURN: { context.pop(); @@ -974,8 +1005,22 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst Type type = Type.getType(cast.desc); Class clazz = PrimitiveUtils.getPrimitiveByName(type.getClassName()); Object provided = context.provider.getField(cast.owner, cast.name, cast.desc, null, context); + if(provided == null && type.getSort() != Type.OBJECT && type.getSort() != Type.ARRAY) + { + ClassNode fieldClass = null; + for(ClassNode cn : context.dictionary.values()) + if(cn.name.equals(cast.owner)) + { + fieldClass = cn; + break; + } + provided = fieldClass.fields.stream().filter(f -> f.name.equals(cast.name) && f.desc.equals(cast.desc)).findFirst().orElse(null).value; + if(type.getSort() == Type.BOOLEAN) + provided = (Integer)provided == 0 ? false : true; + context.provider.setField(cast.owner, cast.name, cast.desc, null, provided, context); + } - switch (type.getReturnType().getSort()) { + switch (type.getSort()) { case Type.BOOLEAN: stack.add(0, new JavaBoolean((Boolean) provided)); break; @@ -1004,14 +1049,35 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst break; case Type.ARRAY: case Type.OBJECT: - stack.add(0, new JavaObject(provided, "java/lang/Object")); - break; + if(provided != null && provided.getClass().isArray()) + { + if(TypeStore.getFieldFromStore(cast.owner, cast.name, cast.desc, null) != null) + { + Entry entry = (Entry)TypeStore.getFieldFromStore(cast.owner, cast.name, cast.desc, null).getKey(); + stack.add(0, new JavaArray(entry.getKey(), entry.getValue())); + }else if(provided != null) + stack.add(0, new JavaArray(provided)); + }else if(TypeStore.getFieldFromStore(cast.owner, cast.name, cast.desc, null) == null) + stack.add(0, JavaValue.valueOf(provided)); + else + stack.add(0, new JavaObject(provided, TypeStore.getFieldFromStore(cast.owner, cast.name, cast.desc, null).getValue())); + break; } break; } case PUTSTATIC: { JavaValue obj = stack.remove(0); + if (obj instanceof JavaTop) { + obj = stack.remove(0); + if (VERIFY && !(obj instanceof JavaDouble) && !(obj instanceof JavaLong)) { + throw new ExecutionException("JavaTop not followed by JavaLong or JavaDouble"); + } + } FieldInsnNode cast = (FieldInsnNode) now; + if(obj instanceof JavaArray) + TypeStore.setFieldToStore(cast.owner, cast.name, cast.desc, null, new AbstractMap.SimpleEntry<>(((JavaArray)obj).getObjectArrayWithValues(), obj.type())); + else if(obj instanceof JavaObject) + TypeStore.setFieldToStore(cast.owner, cast.name, cast.desc, null, new AbstractMap.SimpleEntry<>(convert(value(obj), cast.desc), obj.type())); context.provider.setField(cast.owner, cast.name, cast.desc, null, convert(value(obj), cast.desc), context); break; } @@ -1021,7 +1087,22 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst Type type = Type.getType(cast.desc); Class clazz = PrimitiveUtils.getPrimitiveByName(type.getClassName()); Object provided = context.provider.getField(cast.owner, cast.name, cast.desc, obj, context); - switch (type.getReturnType().getSort()) { + if(provided == null && type.getSort() != Type.OBJECT && type.getSort() != Type.ARRAY) + { + ClassNode fieldClass = null; + for(ClassNode cn : context.dictionary.values()) + if(cn.name.equals(cast.owner)) + { + fieldClass = cn; + break; + } + provided = fieldClass.fields.stream().filter(f -> f.name.equals(cast.name) && f.desc.equals(cast.desc)).findFirst().orElse(null).value; + if(type.getSort() == Type.BOOLEAN) + provided = (Integer)provided == 0 ? false : true; + context.provider.setField(cast.owner, cast.name, cast.desc, obj, provided, context); + } + + switch (type.getSort()) { case Type.BOOLEAN: stack.add(0, new JavaBoolean((Boolean) provided)); break; @@ -1050,15 +1131,36 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst break; case Type.ARRAY: case Type.OBJECT: - stack.add(0, new JavaObject(provided, "java/lang/Object")); + if(provided != null && provided.getClass().isArray()) + { + if(TypeStore.getFieldFromStore(cast.owner, cast.name, cast.desc, obj) != null) + { + Entry entry = (Entry)TypeStore.getFieldFromStore(cast.owner, cast.name, cast.desc, obj).getKey(); + stack.add(0, new JavaArray(entry.getKey(), entry.getValue())); + }else if(provided != null) + stack.add(0, new JavaArray(provided)); + }else if(TypeStore.getFieldFromStore(cast.owner, cast.name, cast.desc, obj) == null) + stack.add(0, JavaValue.valueOf(provided)); + else + stack.add(0, new JavaObject(provided, TypeStore.getFieldFromStore(cast.owner, cast.name, cast.desc, obj).getValue())); break; } break; } case PUTFIELD: { JavaValue obj = stack.remove(0); + if (obj instanceof JavaTop) { + obj = stack.remove(0); + if (VERIFY && !(obj instanceof JavaDouble) && !(obj instanceof JavaLong)) { + throw new ExecutionException("JavaTop not followed by JavaLong or JavaDouble"); + } + } JavaValue instance = stack.remove(0); FieldInsnNode cast = (FieldInsnNode) now; + if(obj instanceof JavaArray) + TypeStore.setFieldToStore(cast.owner, cast.name, cast.desc, instance, new AbstractMap.SimpleEntry<>(((JavaArray)obj).getObjectArrayWithValues(), obj.type())); + else if(obj instanceof JavaObject) + TypeStore.setFieldToStore(cast.owner, cast.name, cast.desc, instance, new AbstractMap.SimpleEntry<>(convert(value(obj), cast.desc), obj.type())); context.provider.setField(cast.owner, cast.name, cast.desc, instance, convert(value(obj), cast.desc), context); break; } @@ -1066,7 +1168,9 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst MethodInsnNode cast = (MethodInsnNode) now; Type type = Type.getReturnType(cast.desc); List args = new ArrayList<>(); - for (Type t1 : Type.getArgumentTypes(cast.desc)) { + List l = new ArrayList<>(Arrays.asList(Type.getArgumentTypes(cast.desc))); + Collections.reverse(l); + for (Type t1 : l) { if (t1.getSort() == Type.LONG || t1.getSort() == Type.DOUBLE) { if (!(stack.get(0) instanceof JavaTop)) { throw new ExecutionException("Expected JavaTop"); @@ -1075,13 +1179,24 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst } args.add(0, stack.remove(0).copy()); } + convertArgs(args, Type.getArgumentTypes(cast.desc)); args.add(stack.remove(0)); - String owner = cast.owner; + if(context.customMethodFunc.containsKey(now)) + { + JavaValue res = context.customMethodFunc.get(now).apply(args, context); + if(type.getSort() == Type.VOID) + break; + stack.add(0, res); + if(res instanceof JavaLong || res instanceof JavaDouble) + stack.add(0, new JavaTop()); + break; + } + String owner = args.get(args.size() - 1).type(); while(true) { try { if (context.provider.canInvokeMethod(owner, cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context)) { Object provided = context.provider.invokeMethod(owner, cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context); - switch (type.getReturnType().getSort()) { + switch (type.getSort()) { case Type.BOOLEAN: stack.add(0, new JavaBoolean((Boolean) provided)); break; @@ -1110,17 +1225,35 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst break; case Type.ARRAY: case Type.OBJECT: - stack.add(0, new JavaObject(provided, "java/lang/Object")); - break; + if(provided != null && provided.getClass().isArray()) + { + if(TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + JavaArray array = (JavaArray)TypeStore.returnObjects.get(Thread.currentThread().getId()); + stack.add(0, new JavaArray(array.value(), array.getTypeArray())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, new JavaArray(provided)); + }else if(provided != null && TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + stack.add(0, new JavaObject(provided, TypeStore.returnObjects.get(Thread.currentThread().getId()).type())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, JavaValue.valueOf(provided)); + break; } } else { - throw new NoSuchMethodHandlerException("Could not find invoker for " + cast.owner + " " + cast.name + cast.desc); + throw new NoSuchMethodHandlerException("Could not find invoker for " + args.get(args.size() - 1).type() + " " + cast.owner + " " + cast.name + cast.desc).setThrownFromInvoke(true); } break; } catch (NoSuchMethodHandlerException | IllegalArgumentException t) { - WrappedClassNode ownerWrappedClass = context.dictionary.get(owner); - if (ownerWrappedClass != null) { - ClassNode ownerClass = ownerWrappedClass.classNode; + if(t instanceof NoSuchMethodHandlerException + && !((NoSuchMethodHandlerException)t).isThrownFromInvoke()) + throw t; + ClassNode ownerClass = context.dictionary.get(owner); + if (ownerClass != null) { if (ownerClass.superName != null) { owner = ownerClass.superName; continue; @@ -1135,7 +1268,9 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst MethodInsnNode cast = (MethodInsnNode) now; Type type = Type.getReturnType(cast.desc); List args = new ArrayList<>(); - for (Type t1 : Type.getArgumentTypes(cast.desc)) { + List l = new ArrayList<>(Arrays.asList(Type.getArgumentTypes(cast.desc))); + Collections.reverse(l); + for (Type t1 : l) { if (t1.getSort() == Type.LONG || t1.getSort() == Type.DOUBLE) { if (!(stack.get(0) instanceof JavaTop)) { throw new ExecutionException("Expected JavaTop"); @@ -1144,13 +1279,24 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst } args.add(0, stack.remove(0).copy()); } + convertArgs(args, Type.getArgumentTypes(cast.desc)); args.add(stack.remove(0)); + if(context.customMethodFunc.containsKey(now)) + { + JavaValue res = context.customMethodFunc.get(now).apply(args, context); + if(type.getSort() == Type.VOID) + break; + stack.add(0, res); + if(res instanceof JavaLong || res instanceof JavaDouble) + stack.add(0, new JavaTop()); + break; + } String owner = cast.owner; while(true) { try { if (context.provider.canInvokeMethod(owner, cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context)) { Object provided = context.provider.invokeMethod(owner, cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context); - switch (type.getReturnType().getSort()) { + switch (type.getSort()) { case Type.BOOLEAN: stack.add(0, new JavaBoolean((Boolean) provided)); break; @@ -1179,17 +1325,35 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst break; case Type.ARRAY: case Type.OBJECT: - stack.add(0, new JavaObject(provided, "java/lang/Object")); + if(provided != null && provided.getClass().isArray()) + { + if(TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + JavaArray array = (JavaArray)TypeStore.returnObjects.get(Thread.currentThread().getId()); + stack.add(0, new JavaArray(array.value(), array.getTypeArray())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, new JavaArray(provided)); + }else if(provided != null && TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + stack.add(0, new JavaObject(provided, TypeStore.returnObjects.get(Thread.currentThread().getId()).type())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, JavaValue.valueOf(provided)); break; } } else { - throw new NoSuchMethodHandlerException("Could not find invoker for " + cast.owner + " " + cast.name + cast.desc); + throw new NoSuchMethodHandlerException("Could not find invoker for " + cast.owner + " " + cast.name + cast.desc).setThrownFromInvoke(true); } break; } catch (NoSuchMethodHandlerException | IllegalArgumentException t) { - WrappedClassNode ownerWrappedClass = context.dictionary.get(owner); - if (ownerWrappedClass != null) { - ClassNode ownerClass = ownerWrappedClass.classNode; + if(t instanceof NoSuchMethodHandlerException + && !((NoSuchMethodHandlerException)t).isThrownFromInvoke()) + throw t; + ClassNode ownerClass = context.dictionary.get(owner); + if (ownerClass != null) { if (ownerClass.superName != null) { owner = ownerClass.superName; continue; @@ -1215,9 +1379,20 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst } args.add(0, stack.remove(0).copy()); } + convertArgs(args, Type.getArgumentTypes(cast.desc)); + if(context.customMethodFunc.containsKey(now)) + { + JavaValue res = context.customMethodFunc.get(now).apply(args, context); + if(type.getSort() == Type.VOID) + break; + stack.add(0, res); + if(res instanceof JavaLong || res instanceof JavaDouble) + stack.add(0, new JavaTop()); + break; + } if (context.provider.canInvokeMethod(cast.owner, cast.name, cast.desc, null, args, context)) { Object provided = context.provider.invokeMethod(cast.owner, cast.name, cast.desc, null, args, context); - switch (type.getReturnType().getSort()) { + switch (type.getSort()) { case Type.BOOLEAN: stack.add(0, new JavaBoolean((Boolean) provided)); break; @@ -1246,11 +1421,27 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst break; case Type.ARRAY: case Type.OBJECT: - stack.add(0, new JavaObject(provided, "java/lang/Object")); + if(provided != null && provided.getClass().isArray()) + { + if(TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + JavaArray array = (JavaArray)TypeStore.returnObjects.get(Thread.currentThread().getId()); + stack.add(0, new JavaArray(array.value(), array.getTypeArray())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, new JavaArray(provided)); + }else if(provided != null && TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + stack.add(0, new JavaObject(provided, TypeStore.returnObjects.get(Thread.currentThread().getId()).type())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, JavaValue.valueOf(provided)); break; } } else { - throw new NoSuchMethodHandlerException("Could not find invoker for " + cast.owner + " " + cast.name + cast.desc); + throw new NoSuchMethodHandlerException("Could not find invoker for " + cast.owner + " " + cast.name + cast.desc).setThrownFromInvoke(true); } break; } @@ -1258,7 +1449,9 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst MethodInsnNode cast = (MethodInsnNode) now; Type type = Type.getReturnType(cast.desc); List args = new ArrayList<>(); - for (Type t1 : Type.getArgumentTypes(cast.desc)) { + List l = new ArrayList<>(Arrays.asList(Type.getArgumentTypes(cast.desc))); + Collections.reverse(l); + for (Type t1 : l) { if (t1.getSort() == Type.LONG || t1.getSort() == Type.DOUBLE) { if (!(stack.get(0) instanceof JavaTop)) { throw new ExecutionException("Expected JavaTop"); @@ -1267,10 +1460,71 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst } args.add(0, stack.remove(0).copy()); } + convertArgs(args, Type.getArgumentTypes(cast.desc)); args.add(stack.remove(0)); - if (context.provider.canInvokeMethod(cast.owner, cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context)) { + if(context.customMethodFunc.containsKey(now)) + { + JavaValue res = context.customMethodFunc.get(now).apply(args, context); + if(type.getSort() == Type.VOID) + break; + stack.add(0, res); + if(res instanceof JavaLong || res instanceof JavaDouble) + stack.add(0, new JavaTop()); + break; + } + if (context.provider.canInvokeMethod(args.get(args.size() - 1).type(), cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context)) { + Object provided = context.provider.invokeMethod(args.get(args.size() - 1).type(), cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context); + switch (type.getSort()) { + case Type.BOOLEAN: + stack.add(0, new JavaBoolean((Boolean) provided)); + break; + case Type.CHAR: + stack.add(0, new JavaCharacter((Character) provided)); + break; + case Type.BYTE: + stack.add(0, new JavaByte((Byte) provided)); + break; + case Type.SHORT: + stack.add(0, new JavaShort((Short) provided)); + break; + case Type.INT: + stack.add(0, new JavaInteger((Integer) provided)); + break; + case Type.FLOAT: + stack.add(0, new JavaFloat((Float) provided)); + break; + case Type.LONG: + stack.add(0, new JavaLong((Long) provided)); + stack.add(0, new JavaTop()); + break; + case Type.DOUBLE: + stack.add(0, new JavaDouble((Double) provided)); + stack.add(0, new JavaTop()); + break; + case Type.ARRAY: + case Type.OBJECT: + if(provided != null && provided.getClass().isArray()) + { + if(TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + JavaArray array = (JavaArray)TypeStore.returnObjects.get(Thread.currentThread().getId()); + stack.add(0, new JavaArray(array.value(), array.getTypeArray())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, new JavaArray(provided)); + }else if(provided != null && TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + stack.add(0, new JavaObject(provided, TypeStore.returnObjects.get(Thread.currentThread().getId()).type())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, JavaValue.valueOf(provided)); + break; + } + } else if (context.provider.canInvokeMethod(cast.owner, cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context)) { Object provided = context.provider.invokeMethod(cast.owner, cast.name, cast.desc, args.get(args.size() - 1), args.subList(0, args.size() - 1), context); - switch (type.getReturnType().getSort()) { + switch (type.getSort()) { case Type.BOOLEAN: stack.add(0, new JavaBoolean((Boolean) provided)); break; @@ -1299,15 +1553,88 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst break; case Type.ARRAY: case Type.OBJECT: - stack.add(0, new JavaObject(provided, "java/lang/Object")); + if(provided != null && provided.getClass().isArray()) + { + if(TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + JavaArray array = (JavaArray)TypeStore.returnObjects.get(Thread.currentThread().getId()); + stack.add(0, new JavaArray(array.value(), array.getTypeArray())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, new JavaArray(provided)); + }else if(provided != null && TypeStore.returnObjects.containsKey(Thread.currentThread().getId()) + && TypeStore.returnObjects.get(Thread.currentThread().getId()).value() == provided) + { + stack.add(0, new JavaObject(provided, TypeStore.returnObjects.get(Thread.currentThread().getId()).type())); + TypeStore.returnObjects.remove(Thread.currentThread().getId()); + }else + stack.add(0, JavaValue.valueOf(provided)); break; } - } else { - throw new NoSuchMethodHandlerException("Could not find invoker for " + cast.owner + " " + cast.name + cast.desc); + }else { + throw new NoSuchMethodHandlerException("Could not find invoker for " + args.get(args.size() - 1).type() + " " + cast.owner + " " + cast.name + cast.desc).setThrownFromInvoke(true); } break; } case INVOKEDYNAMIC: { + if(context.customMethodFunc.containsKey(now)) + { + InvokeDynamicInsnNode cast = (InvokeDynamicInsnNode)now; + List args = new ArrayList<>(); + //First we add bootstrap args, then normal pulled args + args.add(new JavaObject(null, "java/lang/invoke/MethodHandles$Lookup")); // Lookup + args.add(JavaValue.valueOf(cast.name)); + args.add(new JavaObject(cast.desc, "java/lang/invoke/MethodType")); // dyn methodtype + Type[] argumentTypes = Type.getArgumentTypes(cast.bsm.getDesc()); + for(int i = 0; i < cast.bsmArgs.length; i++) + { + Object arg = cast.bsmArgs[i]; + if(arg.getClass() == Type.class) + { + Type type = (Type)arg; + args.add(JavaValue.valueOf(new JavaClass( + type.getInternalName().replace('/', '.'), context))); + }else if(argumentTypes[i + 3].getSort() == Type.BOOLEAN) + args.add(new JavaBoolean((Boolean)arg)); + else if(argumentTypes[i + 3].getSort() == Type.CHAR) + args.add(new JavaCharacter((Character)arg)); + else if(argumentTypes[i + 3].getSort() == Type.BYTE) + args.add(new JavaByte((Byte)arg)); + else if(argumentTypes[i + 3].getSort() == Type.SHORT) + args.add(new JavaShort((Short)arg)); + else if(argumentTypes[i + 3].getSort() == Type.INT) + args.add(new JavaInteger((Integer)arg)); + else if(argumentTypes[i + 3].getSort() == Type.FLOAT) + args.add(new JavaFloat((Float)arg)); + else if(argumentTypes[i + 3].getSort() == Type.LONG) + args.add(new JavaLong((Long)arg)); + else if(argumentTypes[i + 3].getSort() == Type.DOUBLE) + args.add(new JavaDouble((Double)arg)); + else + args.add(JavaValue.valueOf(arg)); + } + List newArgs = new ArrayList<>(); + List l = new ArrayList<>(Arrays.asList(Type.getArgumentTypes(cast.desc))); + Collections.reverse(l); + for (Type t1 : l) { + if (t1.getSort() == Type.LONG || t1.getSort() == Type.DOUBLE) { + if (!(stack.get(0) instanceof JavaTop)) { + throw new ExecutionException("Expected JavaTop"); + } + stack.remove(0); + } + newArgs.add(0, stack.remove(0).copy()); + } + args.addAll(newArgs); + JavaValue res = context.customMethodFunc.get(now).apply(args, context); + if(Type.getReturnType(cast.desc).getSort() == Type.VOID) + break; + stack.add(0, res); + if(res instanceof JavaLong || res instanceof JavaDouble) + stack.add(0, new JavaTop()); + break; + } throw new ExecutionException(new UnsupportedOperationException()); } case NEW: { @@ -1347,12 +1674,12 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst default: throw new ExecutionException("Unknown newarray type " + cast.operand); } - stack.add(0, new JavaObject(add, "java/lang/Object")); + stack.add(0, new JavaArray(add)); break; } case ANEWARRAY: { int len = stack.remove(0).intValue(); - stack.add(0, new JavaObject(new Object[len], "java/lang/Object")); + stack.add(0, new JavaArray(new Object[len])); break; } case ARRAYLENGTH: { @@ -1381,9 +1708,15 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst case CHECKCAST: { TypeInsnNode cast = (TypeInsnNode) now; JavaValue obj = stack.get(0); + Type type; + try { + type = Type.getType(cast.desc); + } catch (Throwable ignored) { + type = Type.getObjectType(cast.desc); + } if (obj.value() != null) { - if (context.provider.canCheckcast(obj, Type.getType(cast.desc), context)) { - if (!context.provider.checkcast(obj, Type.getType(cast.desc), context)) { + if (context.provider.canCheckcast(obj, type, context)) { + if (!context.provider.checkcast(obj, type, context)) { throw new ClassCastException(cast.desc); } } else { @@ -1395,8 +1728,14 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst case INSTANCEOF: { TypeInsnNode cast = (TypeInsnNode) now; JavaValue obj = stack.remove(0); - if (context.provider.canCheckInstanceOf(obj, Type.getType(cast.desc), context)) { - boolean is = context.provider.instanceOf(obj, Type.getType(cast.desc), context); + Type type; + try { + type = Type.getType(cast.desc); + } catch (Throwable ignored) { + type = Type.getObjectType(cast.desc); + } + if (context.provider.canCheckInstanceOf(obj, type, context)) { + boolean is = context.provider.instanceOf(obj, type, context); stack.add(0, new JavaInteger(is ? 1 : 0)); } else { throw new NoSuchComparisonHandlerException("No comparator found for " + cast.desc); @@ -1404,11 +1743,11 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst break; } case MONITORENTER: { - Monitor.enter(stack.remove(0)); + context.monitor.enter(stack.remove(0)); break; } case MONITOREXIT: { - Monitor.exit(stack.remove(0)); + context.monitor.exit(stack.remove(0)); break; } case MULTIANEWARRAY: { @@ -1436,7 +1775,7 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst } } } - stack.add(0, new JavaObject(root, "java/lang/Object")); + stack.add(0, new JavaArray(root)); break; } case IFNULL: { @@ -1474,13 +1813,12 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst if (method.instructions.indexOf(tcbn.start) <= method.instructions.indexOf(now) && method.instructions.indexOf(now) < method.instructions.indexOf(tcbn.end)) { if (tcbn.type == null || tcbn.type.equals("java/lang/Throwable")) { stack.clear(); - stack.add(new JavaObject(toThrow, "java/lang/Object")); + stack.add(JavaValue.valueOf(toThrow)); now = tcbn.handler; continue forever; } else { - WrappedClassNode wr = context.dictionary.get(Type.getType(toThrow.getClass()).getInternalName()); - if (wr.classNode != null) { - ClassNode cn = wr.classNode; + ClassNode cn = context.dictionary.get(Type.getType(toThrow.getClass()).getInternalName()); + if (cn != null) { boolean ok = false; while (cn != null) { if (cn.name.equals(tcbn.type)) { @@ -1490,14 +1828,11 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst if (cn.superName == null) { break; } - wr = context.dictionary.get(cn.superName); - if (wr != null) { - cn = wr.classNode; - } + cn = context.dictionary.get(cn.superName); } if (ok) { stack.clear(); - stack.add(new JavaObject(toThrow, "java/lang/Object")); + stack.add(JavaValue.valueOf(toThrow)); now = tcbn.handler; continue forever; } @@ -1511,9 +1846,11 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst now = now.getNext(); } catch (ExecutionException e) { if (e.clazz.isEmpty()) { - e.clazz = classNode.classNode.name; + e.clazz = classNode.name; e.method = method.name + method.desc; } + if(e instanceof NoSuchMethodHandlerException) + ((NoSuchMethodHandlerException)e).setThrownFromInvoke(false); throw e; } catch (Throwable t) { if (DEBUG_PRINT_EXCEPTIONS) { @@ -1524,13 +1861,12 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst if (method.instructions.indexOf(tcbn.start) <= method.instructions.indexOf(now) && method.instructions.indexOf(now) < method.instructions.indexOf(tcbn.end)) { if (tcbn.type == null || tcbn.type.equals("java/lang/Throwable")) { stack.clear(); - stack.add(new JavaObject(t, "java/lang/Object")); + stack.add(JavaValue.valueOf(t)); now = tcbn.handler; continue forever; } else { - WrappedClassNode wr = context.dictionary.get(Type.getType(t.getClass()).getInternalName()); - if (wr.classNode != null) { - ClassNode cn = wr.classNode; + ClassNode cn = context.dictionary.get(Type.getType(t.getClass()).getInternalName()); + if (cn != null) { boolean ok = false; while (cn != null) { if (cn.name.equals(tcbn.type)) { @@ -1540,14 +1876,11 @@ private static T execute(WrappedClassNode classNode, MethodNode method, Abst if (cn.superName == null) { break; } - wr = context.dictionary.get(cn.superName); - if (wr != null) { - cn = wr.classNode; - } + cn = context.dictionary.get(cn.superName); } if (ok) { stack.clear(); - stack.add(new JavaObject(t, "java/lang/Object")); + stack.add(JavaValue.valueOf(t)); now = tcbn.handler; continue forever; } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/Monitor.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/Monitor.java index e6ea8c75..7ce5dd95 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/Monitor.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/Monitor.java @@ -4,12 +4,14 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; + public class Monitor { - private final static Map locks = new HashMap<>(); - private final static Map counters = new HashMap<>(); + private final Map locks = new HashMap<>(); + private final Map counters = new HashMap<>(); - public static void enter(Object obj) { - if (obj == null) throw new NullPointerException(); + public void enter(Object obj) { + if (((JavaValue)obj).value() == null) throw new NullPointerException(); while (true) { synchronized (locks) { @@ -34,8 +36,8 @@ public static void enter(Object obj) { } } - public static void exit(Object obj) { - if (obj == null) throw new NullPointerException(); + public void exit(Object obj) { + if (((JavaValue)obj).value() == null) throw new NullPointerException(); boolean notify = false; Thread current = Thread.currentThread(); diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/ThreadStore.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/ThreadStore.java index f6fc985b..80bef8e1 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/ThreadStore.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/ThreadStore.java @@ -7,13 +7,13 @@ import java.util.Map; public class ThreadStore { - private static Map threads = Collections.synchronizedMap(new HashMap<>()); + private Map threads = Collections.synchronizedMap(new HashMap<>()); - public static void addThread(long threadId, JavaThread thread) { + public void addThread(long threadId, JavaThread thread) { threads.put(threadId, thread); } - public static JavaThread retrieveThread(long threadId) { + public JavaThread retrieveThread(long threadId) { JavaThread thread = threads.get(threadId); if (thread == null) { // main thread diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/DictionaryMethodProvider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/DictionaryMethodProvider.java index 944a9047..2c29c9a4 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/DictionaryMethodProvider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/DictionaryMethodProvider.java @@ -25,37 +25,35 @@ import com.javadeobfuscator.deobfuscator.executor.providers.MethodProvider; import com.javadeobfuscator.deobfuscator.executor.values.JavaObject; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.ClassNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; public class DictionaryMethodProvider extends MethodProvider { - private final Map classes; + private final Map classes; - public DictionaryMethodProvider(Map classes) { + public DictionaryMethodProvider(Map classes) { this.classes = classes; } public Object invokeMethod(String className, String methodName, String methodDesc, JavaValue targetObject, List args, Context context) { - WrappedClassNode wrappedClassNode = classes.get(className); - if (wrappedClassNode != null) { - ClassNode classNode = wrappedClassNode.classNode; - MethodNode methodNode = classNode.methods.stream().filter(mn -> mn.name.equals(methodName) && mn.desc.equals(methodDesc)).findFirst().orElseGet(null); + ClassNode classNode = classes.get(className); + if (classNode != null) { + MethodNode methodNode = classNode.methods.stream().filter(mn -> mn.name.equals(methodName) && mn.desc.equals(methodDesc)).findFirst().orElse(null); if (methodNode != null) { List argsClone = new ArrayList<>(); for (JavaValue arg : args) { argsClone.add(arg.copy()); } - return MethodExecutor.execute(wrappedClassNode, methodNode, argsClone, targetObject == null ? new JavaObject(null, "java/lang/Object") : targetObject, context); + return MethodExecutor.execute(classNode, methodNode, argsClone, targetObject == null ? new JavaObject(null, "java/lang/Object") : targetObject, context); } } throw new IllegalStateException(); } public boolean canInvokeMethod(String className, String methodName, String methodDesc, JavaValue targetObject, List args, Context context) { - WrappedClassNode wrappedClassNode = classes.get(className); - if (wrappedClassNode != null) { - MethodNode methodNode = wrappedClassNode.classNode.methods.stream().filter(mn -> mn.name.equals(methodName) && mn.desc.equals(methodDesc)).findFirst().orElseGet(null); + ClassNode classNode = classes.get(className); + if (classNode != null) { + MethodNode methodNode = classNode.methods.stream().filter(mn -> mn.name.equals(methodName) && mn.desc.equals(methodDesc)).findFirst().orElse(null); return methodNode != null; } return false; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/JVMComparisonProvider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/JVMComparisonProvider.java index ed26d761..d219eb17 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/JVMComparisonProvider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/JVMComparisonProvider.java @@ -20,7 +20,7 @@ import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaClass; import com.javadeobfuscator.deobfuscator.executor.providers.ComparisonProvider; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; +import org.objectweb.asm.Type; public class JVMComparisonProvider extends ComparisonProvider { @Override diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/JVMMethodProvider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/JVMMethodProvider.java index fd1bff09..c6a4fd0e 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/JVMMethodProvider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/JVMMethodProvider.java @@ -17,24 +17,35 @@ package com.javadeobfuscator.deobfuscator.executor.defined; import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigInteger; +import java.net.URI; import java.net.URL; +import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.security.CodeSource; +import java.security.Key; +import java.security.MessageDigest; import java.security.ProtectionDomain; import java.security.cert.Certificate; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.KeySpec; import java.util.*; import java.util.regex.Pattern; +import java.util.zip.Inflater; +import java.util.zip.InflaterInputStream; +import java.util.zip.ZipFile; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import com.javadeobfuscator.deobfuscator.executor.ThreadStore; import com.javadeobfuscator.deobfuscator.executor.defined.types.*; import com.javadeobfuscator.deobfuscator.executor.exceptions.ExecutionException; import com.javadeobfuscator.deobfuscator.executor.values.*; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.ClassNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; import com.javadeobfuscator.deobfuscator.executor.values.JavaCharacter; import com.javadeobfuscator.deobfuscator.executor.values.JavaInteger; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; @@ -42,13 +53,16 @@ import com.javadeobfuscator.deobfuscator.executor.Context; import com.javadeobfuscator.deobfuscator.executor.providers.MethodProvider; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; - +import org.objectweb.asm.Type; +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; public class JVMMethodProvider extends MethodProvider { - @SuppressWarnings("serial") + @SuppressWarnings({"serial", "CloneableClassWithoutClone"}) //@formatter:off private static final Map, Context, Object>>> functions = new HashMap, Context, Object>>>() {{ // Java @@ -62,10 +76,22 @@ public class JVMMethodProvider extends MethodProvider { }); put("wait(J)V", (targetObject, args, context) -> { synchronized (targetObject.as(Object.class)) { - targetObject.as(Object.class).wait((long) args.get(0).value()); + targetObject.as(Object.class).wait(args.get(0).longValue()); } return null; }); + put("equals(Ljava/lang/Object;)Z", (targetObject, args, context) -> targetObject.value().equals(args.get(0).value())); + put("clone()Ljava/lang/Object;", (targetObject, args, context) -> { + Method clone = Object.class.getDeclaredMethod("clone"); + clone.setAccessible(true); + try + { + return clone.invoke(targetObject.value()); + }catch(InvocationTargetException e) + { + throw e.getTargetException(); + } + }); put("()V", (targetObject, args, context) -> { expect(targetObject, targetObject.type()); targetObject.initialize(new JavaObject(null, targetObject.type())); @@ -73,16 +99,13 @@ public class JVMMethodProvider extends MethodProvider { return null; }); }}); - put("java/util/zip/ZipInputStream", new HashMap, Context, Object>>() {{ - put("(Ljava/io/InputStream;)V", (targetObject, args, context) -> { - System.out.println("New ZipInputStream with " + args.get(0).value()); - targetObject.initialize(new ZipInputStream(args.get(0).as(InputStream.class))); - return null; - }); - }}); put("java/nio/charset/Charset", new HashMap, Context, Object>>() {{ put("availableCharsets()Ljava/util/SortedMap;", (targetObject, args, context) -> Charset.availableCharsets()); }}); + put("java/nio/ByteBuffer", new HashMap, Context, Object>>() {{ + put("wrap([B)Ljava/nio/ByteBuffer;", (targetObject, args, context) -> ByteBuffer.wrap(args.get(0).as(byte[].class))); + put("getDouble()D", (targetObject, args, context) -> targetObject.as(ByteBuffer.class).getDouble()); + }}); put("java/util/SortedMap", new HashMap, Context, Object>>() {{ put("keySet()Ljava/util/Set;", (targetObject, args, context) -> targetObject.as(SortedMap.class).keySet()); }}); @@ -115,14 +138,106 @@ public class JVMMethodProvider extends MethodProvider { } return null; }); + put("write([BII)V", (targetObject, args, context) -> { + try { + targetObject.as(ByteArrayOutputStream.class).write(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue()); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + }); + }}); + put("java/io/ByteArrayInputStream", new HashMap, Context, Object>>() {{ + put("available()I", (targetObject, args, context) -> targetObject.as(ByteArrayInputStream.class).available()); + put("close()V", (targetObject, args, context) -> { + try { + targetObject.as(ByteArrayInputStream.class).close(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + }); + put("read([B)I", (targetObject, args, context) -> targetObject.as(ByteArrayInputStream.class).read(args.get(0).as(byte[].class))); + }}); + put("java/io/PushbackInputStream", new HashMap, Context, Object>>() {{ + put("(Ljava/io/InputStream;I)V", (targetObject, args, context) -> { + targetObject.initialize(new PushbackInputStream(args.get(0).as(InputStream.class), args.get(1).intValue())); + return null; + }); + put("unread([BII)V", (targetObject, args, context) -> { + targetObject.as(PushbackInputStream.class).unread(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue()); + return null; + }); + put("read([BII)I", (targetObject, args, context) -> targetObject.as(PushbackInputStream.class).read(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue())); + }}); + put("java/io/FilterInputStream", new HashMap, Context, Object>>() {{ + put("(Ljava/io/InputStream;)V", (targetObject, args, context) -> { + Constructor init = FilterInputStream.class.getDeclaredConstructor(InputStream.class); + init.setAccessible(true); + targetObject.initialize(init.newInstance(args.get(0).as(InputStream.class))); + return null; + }); + put("read([BII)I", (targetObject, args, context) -> targetObject.as(FilterInputStream.class).read(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue())); + }}); + put("java/util/zip/InflaterInputStream", new HashMap, Context, Object>>() {{ + put("(Ljava/io/InputStream;Ljava/util/zip/Inflater;)V", (targetObject, args, context) -> { + targetObject.initialize(new InflaterInputStream(args.get(0).as(InputStream.class), args.get(1).as(Inflater.class))); + return null; + }); + put("read([B)I", (targetObject, args, context) -> targetObject.as(InflaterInputStream.class).read(args.get(0).as(byte[].class))); + put("read([BII)I", (targetObject, args, context) -> targetObject.as(InflaterInputStream.class).read(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue())); + }}); + put("java/util/zip/Inflater", new HashMap, Context, Object>>() {{ + put("(Z)V", (targetObject, args, context) -> { + targetObject.initialize(new Inflater(args.get(0).as(boolean.class))); + return null; + }); + put("setInput([BII)V", (targetObject, args, context) -> { + targetObject.as(Inflater.class).setInput(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue()); + return null; + }); + }}); + put("java/io/InputStream", new HashMap, Context, Object>>() {{ + put("read([BII)I", (targetObject, args, context) -> targetObject.as(InputStream.class).read(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue())); + }}); + put("java/util/List", new HashMap, Context, Object>>() {{ + put("add(Ljava/lang/Object;)Z", (targetObject, args, context) -> targetObject.as(List.class).add(args.get(0).as(Object.class))); + put("size()I", (targetObject, args, context) -> targetObject.as(List.class).size()); + put("get(I)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(List.class).get(args.get(0).intValue())); + put("set(ILjava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(List.class).set(args.get(0).intValue(), args.get(1).as(Object.class))); + put("toArray()[Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(List.class).toArray()); + put("iterator()Ljava/util/Iterator;", (targetObject, args, context) -> targetObject.as(List.class).iterator()); }}); put("java/util/Arrays", new HashMap, Context, Object>>() {{ put("asList([Ljava/lang/Object;)Ljava/util/List;", (targetObject, args, context) -> Arrays.asList(args.get(0).as(Object[].class))); + put("toString([Ljava/lang/Object;)Ljava/lang/String;", (targetObject, args, context) -> Arrays.toString(args.get(0).as(Object[].class))); + put("copyOf([BI)[B", (targetObject, args, context) -> Arrays.copyOf(args.get(0).as(byte[].class), args.get(1).intValue())); + put("equals([Ljava/lang/Object;[Ljava/lang/Object;)Z", (targetObject, args, context) -> Arrays.equals(args.get(0).as(Object[].class), args.get(1).as(Object[].class))); }}); put("java/util/ArrayList", new HashMap, Context, Object>>() {{ - put("()V", (targetObject, args, context) -> new ArrayList()); + put("()V", (targetObject, args, context) -> { + expect(targetObject, "java/util/ArrayList"); + targetObject.initialize(new ArrayList<>()); + return null; + }); + put("(Ljava/util/Collection;)V", (targetObject, args, context) -> { + expect(targetObject, "java/util/ArrayList"); + targetObject.initialize(new ArrayList<>(args.get(0).as(Collection.class))); + return null; + }); + put("add(Ljava/lang/Object;)Z", (targetObject, args, context) -> targetObject.as(ArrayList.class).add(args.get(0).as(Object.class))); + put("size()I", (targetObject, args, context) -> targetObject.as(ArrayList.class).size()); + put("get(I)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(ArrayList.class).get(args.get(0).intValue())); + put("set(ILjava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(ArrayList.class).set(args.get(0).intValue(), args.get(1).as(Object.class))); + put("toArray()[Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(ArrayList.class).toArray()); + put("iterator()Ljava/util/Iterator;", (targetObject, args, context) -> targetObject.as(ArrayList.class).iterator()); }}); put("java/lang/String", new HashMap, Context, Object>>() {{ + put("(Ljava/lang/StringBuffer;)V", (targetObject, args, context) -> { + expect(targetObject, "java/lang/String"); + targetObject.initialize(new String(args.get(0).as(StringBuffer.class))); + return null; + }); put("([CII)V", (targetObject, args, context) -> { expect(targetObject, "java/lang/String"); targetObject.initialize(new String(args.get(0).as(char[].class), args.get(1).intValue(), args.get(2).intValue())); @@ -143,7 +258,21 @@ public class JVMMethodProvider extends MethodProvider { targetObject.initialize(new String(args.get(0).as(byte[].class), args.get(1).intValue())); return null; }); + put("([BII)V", (targetObject, args, context) -> { + expect(targetObject, "java/lang/String"); + targetObject.initialize(new String(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue())); + return null; + }); + put("([BLjava/lang/String;)V", (targetObject, args, context) -> { + expect(targetObject, "java/lang/String"); + targetObject.initialize(new String(args.get(0).as(byte[].class), args.get(1).as(String.class))); + return null; + }); + put("getBytes()[B", (targetObject, args, context) -> targetObject.as(String.class).getBytes()); + put("getBytes(Ljava/nio/charset/Charset;)[B", (targetObject, args, context) -> targetObject.as(String.class).getBytes(args.get(0).as(Charset.class))); + put("toString()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(String.class).toString()); put("intern()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(String.class).intern()); + put("valueOf(I)Ljava/lang/String;", (targetObject, args, context) -> String.valueOf(args.get(0).intValue())); put("equals(Ljava/lang/Object;)Z", (targetObject, args, context) -> targetObject.as(String.class).equals(args.get(0).value())); put("trim()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(String.class).trim()); put("toCharArray()[C", (targetObject, args, context) -> targetObject.as(String.class).toCharArray()); @@ -155,18 +284,19 @@ public class JVMMethodProvider extends MethodProvider { put("startsWith(Ljava/lang/String;)Z", (targetObject, args, context) -> targetObject.as(String.class).startsWith(args.get(0).as(String.class))); put("substring(I)Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(String.class).substring(args.get(0).intValue())); put("substring(II)Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(String.class).substring(args.get(0).intValue(), args.get(1).intValue())); + put("indexOf(Ljava/lang/String;)I", (targetObject, args, context) -> targetObject.as(String.class).indexOf(args.get(0).as(String.class))); put("indexOf(II)I", (targetObject, args, context) -> targetObject.as(String.class).indexOf(args.get(0).intValue(), args.get(1).intValue())); + put("lastIndexOf(Ljava/lang/String;)I", (targetObject, args, context) -> targetObject.as(String.class).lastIndexOf(args.get(0).as(String.class))); put("lastIndexOf(I)I", (targetObject, args, context) -> targetObject.as(String.class).lastIndexOf(args.get(0).intValue())); + put("isEmpty()Z", (targetObject, args, context) -> targetObject.as(String.class).isEmpty()); + put("format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;", (targetObject, args, context) -> String.format(args.get(0).as(String.class), args.get(1).as(Object[].class))); put("split(Ljava/lang/String;)[Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(String.class).split(args.get(0).as(String.class))); put("valueOf(Ljava/lang/Object;)Ljava/lang/String;", (targetObject, args, context) -> String.valueOf(args.get(0).value())); - put("getBytes(Ljava/lang/String;)[B", (targetObject, args, context) -> { - try { - return targetObject.as(String.class).getBytes(args.get(0).as(String.class)); - } catch (UnsupportedEncodingException e) { - throw new ExecutionException(e); - } - }); + put("valueOf([C)Ljava/lang/String;", (targetObject, args, context) -> String.valueOf(args.get(0).as(char[].class))); + put("replaceAll(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(String.class).replaceAll(args.get(0).as(String.class), args.get(1).as(String.class))); + put("getBytes(Ljava/lang/String;)[B", (targetObject, args, context) -> targetObject.as(String.class).getBytes(args.get(0).as(String.class))); put("valueOf([CII)Ljava/lang/String;", (targetObject, args, context) -> String.valueOf(args.get(0).as(char[].class), args.get(1).intValue(), args.get(2).intValue())); + put("replace(CC)Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(String.class).replace(args.get(0).as(char.class), args.get(1).as(char.class))); }}); put("java/lang/StringBuilder", new HashMap, Context, Object>>() {{ put("()V", (targetObject, args, context) -> { @@ -174,6 +304,11 @@ public class JVMMethodProvider extends MethodProvider { targetObject.initialize(new StringBuilder()); return null; }); + put("(I)V", (targetObject, args, context) -> { + expect(targetObject, "java/lang/StringBuilder"); + targetObject.initialize(new StringBuilder(args.get(0).intValue())); + return null; + }); put("(Ljava/lang/String;)V", (targetObject, args, context) -> { expect(targetObject, "java/lang/StringBuilder"); targetObject.initialize(new StringBuilder(args.get(0).as(String.class))); @@ -183,6 +318,7 @@ public class JVMMethodProvider extends MethodProvider { put("append(C)Ljava/lang/StringBuilder;", (targetObject, args, context) -> targetObject.as(StringBuilder.class).append(args.get(0).as(char.class))); put("append(Ljava/lang/String;)Ljava/lang/StringBuilder;", (targetObject, args, context) -> targetObject.as(StringBuilder.class).append(args.get(0).as(String.class))); put("append(Ljava/lang/Object;)Ljava/lang/StringBuilder;", (targetObject, args, context) -> targetObject.as(StringBuilder.class).append(args.get(0).as(Object.class))); + put("reverse()Ljava/lang/StringBuilder;", (targetObject, args, context) -> targetObject.as(StringBuilder.class).reverse()); put("toString()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(StringBuilder.class).toString()); put("length()I", (targetObject, args, context) -> targetObject.as(StringBuilder.class).length()); put("charAt(I)C", (targetObject, args, context) -> targetObject.as(StringBuilder.class).charAt(args.get(0).intValue())); @@ -212,6 +348,11 @@ public class JVMMethodProvider extends MethodProvider { put("append(C)Ljava/lang/StringBuffer;", (targetObject, args, context) -> targetObject.as(StringBuffer.class).append(args.get(0).as(char.class))); put("toString()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(StringBuffer.class).toString()); }}); + put("java/lang/CharSequence", new HashMap, Context, Object>>() {{ + put("toString()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(CharSequence.class).toString()); + put("length()I", (targetObject, args, context) -> targetObject.as(CharSequence.class).length()); + put("charAt(I)C", (targetObject, args, context) -> targetObject.as(CharSequence.class).charAt(args.get(0).intValue())); + }}); put("java/lang/Exception", new HashMap, Context, Object>>() {{ put("()V", (targetObject, args, context) -> { expect(targetObject, "java/lang/Exception"); @@ -242,7 +383,7 @@ public class JVMMethodProvider extends MethodProvider { put("java/lang/RuntimeException", new HashMap, Context, Object>>() {{ put("(Ljava/lang/String;)V", (targetObject, args, context) -> { expect(targetObject, "java/lang/RuntimeException"); - targetObject.initialize(args.get(0).value()); + targetObject.initialize(new RuntimeException(args.get(0).as(String.class))); return null; }); put("()V", (targetObject, args, context) -> { @@ -273,6 +414,8 @@ public class JVMMethodProvider extends MethodProvider { put("getInterfaces()[Ljava/lang/Class;", (targetObject, args, context) -> targetObject.as(JavaClass.class).getInterfaces()); put("getProtectionDomain()Ljava/security/ProtectionDomain;", (targetObject, args, context) -> new ProtectionDomain(new CodeSource(context.file.toURI().toURL(), new Certificate[0]), null)); put("isInterface()Z", (targetObject, args, context) -> targetObject.as(JavaClass.class).isInterface()); + put("isArray()Z", (targetObject, args, context) -> targetObject.as(JavaClass.class).getType().getSort() == Type.ARRAY); + put("equals(Ljava/lang/Object;)Z", (targetObject, args, context) -> targetObject.as(JavaClass.class).equals(args.get(0).as(JavaClass.class))); }}); put("java/security/ProtectionDomain", new HashMap, Context, Object>>() {{ put("getCodeSource()Ljava/security/CodeSource;", (targetObject, args, context) -> targetObject.as(ProtectionDomain.class).getCodeSource()); @@ -280,7 +423,18 @@ public class JVMMethodProvider extends MethodProvider { put("java/security/CodeSource", new HashMap, Context, Object>>() {{ put("getLocation()Ljava/net/URL;", (targetObject, args, context) -> targetObject.as(CodeSource.class).getLocation()); }}); + put("java/security/MessageDigest", new HashMap, Context, Object>>() {{ + put("getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;", (targetObject, args, context) -> MessageDigest.getInstance(args.get(0).as(String.class))); + put("digest([B)[B", (targetObject, args, context) -> targetObject.as(MessageDigest.class).digest(args.get(0).as(byte[].class))); + }}); + put("java/security/MessageDigest$Delegate", new HashMap, Context, Object>>() {{ + put("digest([B)[B", (targetObject, args, context) -> ((MessageDigest)targetObject.as(Class.forName("java.security.MessageDigest$Delegate"))).digest(args.get(0).as(byte[].class))); + }}); + put("java/security/MessageDigest$Delegate$CloneableDelegate", new HashMap, Context, Object>>() {{ + put("digest([B)[B", (targetObject, args, context) -> ((MessageDigest)targetObject.as(Class.forName("java.security.MessageDigest$Delegate$CloneableDelegate"))).digest(args.get(0).as(byte[].class))); + }}); put("java/net/URL", new HashMap, Context, Object>>() {{ + put("toURI()Ljava/net/URI;", (targetObject, args, context) -> targetObject.as(URL.class).toURI()); // Probably not an issue because you can't construct URLs yet put("openStream()Ljava/io/InputStream;", (targetObject, args, context) -> { URL url = targetObject.as(URL.class); @@ -290,6 +444,37 @@ public class JVMMethodProvider extends MethodProvider { throw new ExecutionException("Disallowed opening URL for now"); }); }}); + put("java/net/URI", new HashMap, Context, Object>>() {{ + put("getPath()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(URI.class).getPath()); + }}); + put("java/io/File", new HashMap, Context, Object>>() {{ + put("(Ljava/lang/String;)V", (targetObject, args, context) -> { + expect(targetObject, "java/io/File"); + targetObject.initialize(new File(args.get(0).as(String.class))); + return null; + }); + }}); + put("java/util/zip/ZipFile", new HashMap, Context, Object>>() {{ + put("(Ljava/lang/String;)V", (targetObject, args, context) -> { + expect(targetObject, "java/util/zip/ZipFile"); + targetObject.initialize(new ZipFile(args.get(0).as(String.class))); + return null; + }); + put("(Ljava/io/File;)V", (targetObject, args, context) -> { + expect(targetObject, "java/util/zip/ZipFile"); + targetObject.initialize(new ZipFile(args.get(0).as(File.class))); + return null; + }); + put("entries()Ljava/util/Enumeration;", (targetObject, args, context) -> targetObject.as(ZipFile.class).entries()); + put("close()V", (targetObject, args, context) -> { + targetObject.as(ZipFile.class).close(); + return null; + }); + }}); + put("java/util/Enumeration", new HashMap, Context, Object>>() {{ + put("hasMoreElements()Z", (targetObject, args, context) -> targetObject.as(Enumeration.class).hasMoreElements()); + put("nextElement()Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(Enumeration.class).nextElement()); + }}); put("java/util/zip/ZipInputStream", new HashMap, Context, Object>>() {{ put("(Ljava/io/InputStream;)V", (targetObject, args, context) -> { expect(targetObject, "java/util/zip/ZipInputStream"); @@ -305,6 +490,8 @@ public class JVMMethodProvider extends MethodProvider { put("java/util/zip/ZipEntry", new HashMap, Context, Object>>() {{ put("getName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(ZipEntry.class).getName()); put("getExtra()[B", (targetObject, args, context) -> targetObject.as(ZipEntry.class).getExtra()); + put("getLastAccessTime()Ljava/nio/file/attribute/FileTime;", (targetObject, args, context) -> targetObject.as(ZipEntry.class).getLastAccessTime()); + put("getCreationTime()Ljava/nio/file/attribute/FileTime;", (targetObject, args, context) -> targetObject.as(ZipEntry.class).getCreationTime()); }}); put("java/lang/reflect/Constructor", new HashMap, Context, Object>>() {{ put("getParameterTypes()[Ljava/lang/Class;", (targetObject, args, context) -> targetObject.as(JavaConstructor.class).getParameterTypes()); @@ -313,7 +500,7 @@ public class JVMMethodProvider extends MethodProvider { return null; }); - put("newInstance([Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(JavaConstructor.class).newInstance(context, args.get(0).as(Object[].class))); + put("newInstance([Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(JavaConstructor.class).newInstance(context, args.get(0))); }}); put("java/lang/reflect/Method", new HashMap, Context, Object>>() {{ put("getName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(JavaMethod.class).getName()); @@ -324,7 +511,14 @@ public class JVMMethodProvider extends MethodProvider { return null; }); put("hashCode()I", (targetObject, args, context) -> targetObject.as(JavaMethod.class).hashCode()); - put("invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(JavaMethod.class).invoke(args.get(0), args.get(1).as(Object[].class))); + put("invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> { + context.push("java.lang.reflect.Method", "invoke", 0); + context.push("sun.reflect.DelegatingMethodAccessorImpl", "invoke", 0); + context.push("sun.reflect.NativeMethodAccessorImpl", "invoke", 0); + context.push("sun.reflect.NativeMethodAccessorImpl", "invoke0", 0); + return targetObject.as(JavaMethod.class).invoke(args.get(0), args.get(1), context); + }); + put("getDeclaringClass()Ljava/lang/Class;", (targetObject, args, context) -> targetObject.as(JavaMethod.class).getDeclaringClass()); }}); put("java/lang/reflect/Field", new HashMap, Context, Object>>() {{ put("getName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(JavaField.class).getName()); @@ -343,6 +537,7 @@ public class JVMMethodProvider extends MethodProvider { return null; }); put("get(Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(JavaField.class).get(args.get(0).as(Object.class))); + put("getDeclaringClass()Ljava/lang/Class;", (targetObject, args, context) -> targetObject.as(JavaField.class).getDeclaringClass()); }}); put("java/lang/reflect/Modifier", new HashMap, Context, Object>>() {{ put("isStatic(I)Z", (targetObject, args, context) -> Modifier.isStatic(args.get(0).intValue())); @@ -350,15 +545,72 @@ public class JVMMethodProvider extends MethodProvider { }}); put("java/lang/invoke/MethodType", new HashMap, Context, Object>>() {{ put("fromMethodDescriptorString(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", (targetObject, args, context) -> args.get(0).value()); + put("methodType(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;", (targetObject, args, context) -> { + JavaClass[] arguments = toJavaClass(args.get(1).as(Object[].class)); + Type[] types = new Type[arguments.length]; + for(int i = 0; i < arguments.length; i++) + types[i] = arguments[i].getType(); + return Type.getMethodDescriptor(args.get(0).as(JavaClass.class).getType(), types); + }); + put("methodType(Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;", (targetObject, args, context) -> { + JavaClass[] array = new JavaClass[args.get(2).as(Object[].class).length + 1]; + array[0] = args.get(1).as(JavaClass.class); + System.arraycopy(toJavaClass(args.get(2).as(Object[].class)), 0, array, 1, args.get(2).as(Object[].class).length); + Type[] types = new Type[array.length]; + for(int i = 0; i < array.length; i++) + types[i] = array[i].getType(); + return Type.getMethodDescriptor(args.get(0).as(JavaClass.class).getType(), types); + }); + put("parameterCount()I", (targetObject, args, context) -> { + Type[] type = Type.getArgumentTypes(targetObject.as(String.class)); + return type.length; + }); + put("dropParameterTypes(II)Ljava/lang/invoke/MethodType;", (targetObject, args, context) -> { + Type[] type = Type.getArgumentTypes(targetObject.as(String.class)); + int start = args.get(0).intValue(); + int end = args.get(1).intValue(); + int len = type.length; + if(!(0 <= start && start <= end && end <= len)) + throw new IndexOutOfBoundsException("start=" + start +" end=" + end); + Type[] copy; + if(start == 0) + { + if(end == len) + copy = new Type[0]; + else + copy = Arrays.copyOfRange(type, end, len); + }else + { + if(end == len) + copy = Arrays.copyOfRange(type, 0, start); + else + { + int tail = len - end; + copy = Arrays.copyOfRange(type, 0, start + tail); + System.arraycopy(type, end, copy, start, tail); + } + } + return Type.getMethodDescriptor(Type.getReturnType(targetObject.as(String.class)), copy); + }); }}); put("java/lang/invoke/MethodHandles$Lookup", new HashMap, Context, Object>>() {{ put("findStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaMethodHandle(args.get(0).as(JavaClass.class).getType().getInternalName(), args.get(1).as(String.class), args.get(2).as(String.class), "static")); put("findVirtual(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaMethodHandle(args.get(0).as(JavaClass.class).getType().getInternalName(), args.get(1).as(String.class), args.get(2).as(String.class), "virtual")); + put("findSpecial(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaMethodHandle(args.get(0).as(JavaClass.class).getType().getInternalName(), args.get(1).as(String.class), args.get(2).as(String.class), "special")); put("unreflect(Ljava/lang/reflect/Method;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaMethodHandle(args.get(0).as(JavaMethod.class).getDeclaringClass().getName().replace(".", "/"), args.get(0).as(JavaMethod.class).getName(), args.get(0).as(JavaMethod.class).getDesc(), args.get(0).as(JavaMethod.class).isStatic() ? "static" : "virtual")); + put("findStaticGetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaFieldHandle(args.get(0).as(JavaClass.class).getType().getInternalName(), args.get(1).as(String.class), args.get(2).as(JavaClass.class).getType().getDescriptor(), "static", false)); + put("findGetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaFieldHandle(args.get(0).as(JavaClass.class).getType().getInternalName(), args.get(1).as(String.class), args.get(2).as(JavaClass.class).getType().getDescriptor(), "virtual", false)); + put("findStaticSetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaFieldHandle(args.get(0).as(JavaClass.class).getType().getInternalName(), args.get(1).as(String.class), args.get(2).as(JavaClass.class).getType().getDescriptor(), "static", true)); + put("findSetter(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaFieldHandle(args.get(0).as(JavaClass.class).getType().getInternalName(), args.get(1).as(String.class), args.get(2).as(JavaClass.class).getType().getDescriptor(), "virtual", true)); + put("unreflectGetter(Ljava/lang/reflect/Field;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaFieldHandle(args.get(0).as(JavaField.class).getDeclaringClass().getName().replace(".", "/"), args.get(0).as(JavaField.class).getName(), args.get(0).as(JavaField.class).getDesc(), Modifier.isStatic(args.get(0).as(JavaField.class).getModifiers()) ? "static" : "virtual", false)); + put("unreflectSetter(Ljava/lang/reflect/Field;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> new JavaFieldHandle(args.get(0).as(JavaField.class).getDeclaringClass().getName().replace(".", "/"), args.get(0).as(JavaField.class).getName(), args.get(0).as(JavaField.class).getDesc(), Modifier.isStatic(args.get(0).as(JavaField.class).getModifiers()) ? "static" : "virtual", true)); }}); put("java/lang/invoke/MethodHandle", new HashMap, Context, Object>>() {{ put("asType(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> targetObject.value()); }}); + put("java/lang/invoke/MethodHandles", new HashMap, Context, Object>>() {{ + put("dropArguments(Ljava/lang/invoke/MethodHandle;I[Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> args.get(0).value()); + }}); put("java/lang/invoke/ConstantCallSite", new HashMap, Context, Object>>() {{ put("(Ljava/lang/invoke/MethodHandle;)V", (targetObject, args, context) -> { expect(targetObject, "java/lang/invoke/ConstantCallSite"); @@ -372,9 +624,7 @@ public class JVMMethodProvider extends MethodProvider { targetObject.initialize(args.get(0).value()); return null; }); - put("setTarget(Ljava/lang/invoke/MethodHandle;)V", (targetObject, args, context) -> { - return null; - }); + put("setTarget(Ljava/lang/invoke/MethodHandle;)V", (targetObject, args, context) -> null); put("getTarget()Ljava/lang/invoke/MethodHandle;", (targetObject, args, context) -> targetObject.value()); }}); put("java/lang/System", new HashMap, Context, Object>>() {{ @@ -389,7 +639,8 @@ public class JVMMethodProvider extends MethodProvider { targetObject.as(JavaThread.class).start(); return null; }); - put("currentThread()Ljava/lang/Thread;", (targetObject, args, context) -> ThreadStore.retrieveThread(Thread.currentThread().getId())); + put("currentThread()Ljava/lang/Thread;", (targetObject, args, context) -> context.threadStore.retrieveThread(Thread.currentThread().getId())); + put("getId()J", (targetObject, args, context) -> targetObject.as(JavaThread.class).getThread().getId()); put("getStackTrace()[Ljava/lang/StackTraceElement;", (targetObject, args, context) -> { context.push("java.lang.Thread", "getStackTrace", 0); StackTraceElement[] elems = context.getStackTrace(); @@ -412,22 +663,61 @@ public class JVMMethodProvider extends MethodProvider { put("java/lang/StackTraceElement", new HashMap, Context, Object>>() {{ put("getClassName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(StackTraceElement.class).getClassName()); put("getMethodName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(StackTraceElement.class).getMethodName()); + put("getFileName()Ljava/lang/String;", (targetObject, args, context) -> targetObject.as(StackTraceElement.class).getFileName()); + }}); + put("java/lang/Float", new HashMap, Context, Object>>() {{ + put("intBitsToFloat(I)F", (targetObject, args, context) -> Float.intBitsToFloat(args.get(0).intValue())); + put("valueOf(F)Ljava/lang/Float;", (targetObject, args, context) -> Float.valueOf(args.get(0).floatValue())); + }}); + put("java/lang/Double", new HashMap, Context, Object>>() {{ + put("longBitsToDouble(J)D", (targetObject, args, context) -> Double.longBitsToDouble(args.get(0).longValue())); + put("valueOf(D)Ljava/lang/Double;", (targetObject, args, context) -> Double.valueOf(args.get(0).doubleValue())); }}); put("java/lang/Long", new HashMap, Context, Object>>() {{ + put("(J)V", (targetObject, args, context) -> { + expect(targetObject, "java/lang/Long"); + targetObject.initialize(new Long(args.get(0).longValue())); + return null; + }); put("parseLong(Ljava/lang/String;)J", (targetObject, args, context) -> Long.parseLong(args.get(0).as(String.class))); put("parseLong(Ljava/lang/String;I)J", (targetObject, args, context) -> Long.parseLong(args.get(0).as(String.class), args.get(1).intValue())); put("valueOf(J)Ljava/lang/Long;", (targetObject, args, context) -> Long.valueOf(args.get(0).longValue())); + put("valueOf(Ljava/lang/String;)Ljava/lang/Long;", (targetObject, args, context) -> Long.valueOf(args.get(0).as(String.class))); + put("longValue()J", (targetObject, args, context) -> ((Long)targetObject.value()).longValue()); + put("intValue()I", (targetObject, args, context) -> ((Long)targetObject.value()).intValue()); }}); put("java/lang/Integer", new HashMap, Context, Object>>() {{ - put("parseInt(Ljava/lang/String;)I", (targetObject, args, context) -> Integer.parseInt(args.get(0).as(String.class))); - put("valueOf(Ljava/lang/String;)Ljava/lang/Integer;", (targetObject, args, context) -> new JavaInteger(Integer.valueOf(args.get(0).as(String.class)))); - put("valueOf(Ljava/lang/String;I)Ljava/lang/Integer;", (targetObject, args, context) -> new JavaInteger(Integer.valueOf(args.get(0).as(String.class), args.get(1).intValue()))); - put("valueOf(I)Ljava/lang/Integer;", (targetObject, args, context) -> args.get(0)); - put("intValue()I", (targetObject, args, context) -> ((JavaInteger) targetObject.value()).value()); + put("(I)V", (targetObject, args, context) -> { + expect(targetObject, "java/lang/Integer"); + targetObject.initialize(new Integer(args.get(0).intValue())); + return null; + }); + put("parseInt(Ljava/lang/String;)I", (targetObject, args, context) -> Integer.parseInt(args.get(0).as(String.class))); + put("parseInt(Ljava/lang/String;I)I", (targetObject, args, context) -> Integer.parseInt(args.get(0).as(String.class), args.get(1).intValue())); + put("valueOf(Ljava/lang/String;)Ljava/lang/Integer;", (targetObject, args, context) -> Integer.valueOf(args.get(0).as(String.class))); + put("valueOf(Ljava/lang/String;I)Ljava/lang/Integer;", (targetObject, args, context) -> Integer.valueOf(args.get(0).as(String.class), args.get(1).intValue())); + put("valueOf(I)Ljava/lang/Integer;", (targetObject, args, context) -> Integer.valueOf(args.get(0).intValue())); + put("intValue()I", (targetObject, args, context) -> ((Integer)targetObject.value()).intValue()); + }}); + put("java/lang/Character", new HashMap, Context, Object>>() {{ + put("valueOf(C)Ljava/lang/Character;", (targetObject, args, context) -> Character.valueOf(args.get(0).as(char.class))); + put("charValue()C", (targetObject, args, context) -> ((Character)targetObject.value()).charValue()); + }}); + put("java/lang/Boolean", new HashMap, Context, Object>>() {{ + put("valueOf(Z)Ljava/lang/Boolean;", (targetObject, args, context) -> Boolean.valueOf(args.get(0).as(boolean.class))); + put("booleanValue()Z", (targetObject, args, context) -> ((Boolean)targetObject.value()).booleanValue()); }}); put("java/util/regex/Pattern", new HashMap, Context, Object>>() {{ put("compile(Ljava/lang/String;)Ljava/util/regex/Pattern;", (targetObject, args, context) -> Pattern.compile(args.get(0).as(String.class))); }}); + put("java/util/Random", new HashMap, Context, Object>>() {{ + put("(J)V", (targetObject, args, context) -> { + expect(targetObject, "java/util/Random"); + targetObject.initialize(new Random(args.get(0).longValue())); + return null; + }); + put("nextDouble()D", (targetObject, args, context) -> targetObject.as(Random.class).nextDouble()); + }}); put("java/lang/BootstrapMethodError", new HashMap, Context, Object>>() {{ put("()V", (targetObject, args, context) -> { expect(targetObject, "java/lang/BootstrapMethodError"); @@ -450,10 +740,12 @@ public class JVMMethodProvider extends MethodProvider { }); put("(I)V", (targetObject, args, context) -> { expect(targetObject, "java/util/HashMap"); - targetObject.initialize(new HashMap<>(args.get(0).as(Integer.class))); + targetObject.initialize(new HashMap<>(args.get(0).intValue())); return null; }); - put("put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(HashMap.class).put(args.get(0), args.get(1))); + put("put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(HashMap.class).put(args.get(0).value(), args.get(1).value())); + put("get(Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(HashMap.class).get(args.get(0).value())); + put("containsKey(Ljava/lang/Object;)Z", (targetObject, args, context) -> targetObject.as(HashMap.class).containsKey(args.get(0).value())); put("isEmpty()Z", (targetObject, args, context) -> targetObject.as(HashMap.class).isEmpty()); }}); put("java/util/HashSet", new HashMap, Context, Object>>() {{ @@ -470,6 +762,11 @@ public class JVMMethodProvider extends MethodProvider { return null; }); }}); + put("java/util/Map", new HashMap, Context, Object>>() {{ + put("containsKey(Ljava/lang/Object;)Z", (targetObject, args, context) -> targetObject.as(Map.class).containsKey(args.get(0).value())); + put("put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(Map.class).put(args.get(0).value(), args.get(1).value())); + put("get(Ljava/lang/Object;)Ljava/lang/Object;", (targetObject, args, context) -> targetObject.as(Map.class).get(args.get(0).value())); + }}); put("java/util/LinkedHashMap", new HashMap, Context, Object>>() {{ put("()V", (targetObject, args, context) -> { expect(targetObject, "java/util/LinkedHashMap"); @@ -495,23 +792,26 @@ public class JVMMethodProvider extends MethodProvider { }}); put("java/lang/Math", new HashMap, Context, Object>>() {{ put("abs(J)J", (targetObject, args, context) -> Math.abs(args.get(0).longValue())); + put("round(D)J", (targetObject, args, context) -> Math.round(args.get(0).doubleValue())); }}); put("java/math/BigInteger", new HashMap, Context, Object>>() {{ put("(Ljava/lang/String;I)V", (targetObject, args, context) -> { expect(targetObject, "java/math/BigInteger"); - if (args.get(1).value() instanceof Byte) { // FIXME - targetObject.initialize(new BigInteger(args.get(0).as(String.class), (byte) args.get(1).value())); - } else if (args.get(1).value() instanceof Short) { - targetObject.initialize(new BigInteger(args.get(0).as(String.class), (short) args.get(1).value())); - } else { - targetObject.initialize(new BigInteger(args.get(0).as(String.class), (int) args.get(1).value())); - } + targetObject.initialize(new BigInteger(args.get(0).as(String.class), args.get(1).intValue())); return null; }); put("add(Ljava/math/BigInteger;)Ljava/math/BigInteger;", (targetObject, args, context) -> targetObject.as(BigInteger.class).add(args.get(0).as(BigInteger.class))); put("xor(Ljava/math/BigInteger;)Ljava/math/BigInteger;", (targetObject, args, context) -> targetObject.as(BigInteger.class).xor(args.get(0).as(BigInteger.class))); put("modPow(Ljava/math/BigInteger;Ljava/math/BigInteger;)Ljava/math/BigInteger;", (targetObject, args, context) -> targetObject.as(BigInteger.class).modPow(args.get(0).as(BigInteger.class), args.get(1).as(BigInteger.class))); put("intValue()I", (targetObject, args, context) -> new JavaInteger(targetObject.as(BigInteger.class).intValue()).intValue()); + put("valueOf(J)Ljava/math/BigInteger;", (targetObject, args, context) -> BigInteger.valueOf(args.get(0).longValue())); + }}); + put("java/util/Base64", new HashMap, Context, Object>>() {{ + put("getDecoder()Ljava/util/Base64$Decoder;", (targetObject, args, context) -> Base64.getDecoder()); + }}); + put("java/util/Base64$Decoder", new HashMap, Context, Object>>() {{ + put("decode(Ljava/lang/String;)[B", (targetObject, args, context) -> targetObject.as(Base64.Decoder.class).decode(args.get(0).as(String.class))); + put("decode([B)[B", (targetObject, args, context) -> targetObject.as(Base64.Decoder.class).decode(args.get(0).as(byte[].class))); }}); // Javax @@ -519,13 +819,55 @@ public class JVMMethodProvider extends MethodProvider { put("parseBase64Binary(Ljava/lang/String;)[B", (targetObject, args, context) -> DatatypeConverter.parseBase64Binary(args.get(0).as(String.class))); put("parseHexBinary(Ljava/lang/String;)[B", (targetObject, args, context) -> DatatypeConverter.parseHexBinary(args.get(0).as(String.class))); }}); + put("javax/crypto/spec/SecretKeySpec", new HashMap, Context, Object>>() {{ + put("([BLjava/lang/String;)V", (targetObject, args, context) -> { + expect(targetObject, "javax/crypto/spec/SecretKeySpec"); + targetObject.initialize(new SecretKeySpec(args.get(0).as(byte[].class), args.get(1).as(String.class))); + return null; + }); + put("([BIILjava/lang/String;)V", (targetObject, args, context) -> { + expect(targetObject, "javax/crypto/spec/SecretKeySpec"); + targetObject.initialize(new SecretKeySpec(args.get(0).as(byte[].class), args.get(1).intValue(), args.get(2).intValue(), args.get(3).as(String.class))); + return null; + }); + }}); + put("javax/crypto/spec/DESKeySpec", new HashMap, Context, Object>>() {{ + put("([B)V", (targetObject, args, context) -> { + expect(targetObject, "javax/crypto/spec/DESKeySpec"); + targetObject.initialize(new DESKeySpec(args.get(0).as(byte[].class))); + return null; + }); + }}); + put("javax/crypto/spec/IvParameterSpec", new HashMap, Context, Object>>() {{ + put("([B)V", (targetObject, args, context) -> { + expect(targetObject, "javax/crypto/spec/IvParameterSpec"); + targetObject.initialize(new IvParameterSpec(args.get(0).as(byte[].class))); + return null; + }); + }}); + put("javax/crypto/Cipher", new HashMap, Context, Object>>() {{ + put("getInstance(Ljava/lang/String;)Ljavax/crypto/Cipher;", (targetObject, args, context) -> Cipher.getInstance(args.get(0).as(String.class))); + put("init(ILjava/security/Key;)V", (targetObject, args, context) -> { + targetObject.as(Cipher.class).init(args.get(0).intValue(), args.get(1).as(Key.class)); + return null; + }); + put("init(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V", (targetObject, args, context) -> { + targetObject.as(Cipher.class).init(args.get(0).intValue(), args.get(1).as(Key.class), args.get(2).as(AlgorithmParameterSpec.class)); + return null; + }); + put("doFinal([B)[B", (targetObject, args, context) -> targetObject.as(Cipher.class).doFinal(args.get(0).as(byte[].class))); + }}); + put("javax/crypto/SecretKeyFactory", new HashMap, Context, Object>>() {{ + put("getInstance(Ljava/lang/String;)Ljavax/crypto/SecretKeyFactory;", (targetObject, args, context) -> SecretKeyFactory.getInstance(args.get(0).as(String.class))); + put("generateSecret(Ljava/security/spec/KeySpec;)Ljavax/crypto/SecretKey;", (targetObject, args, context) -> targetObject.as(SecretKeyFactory.class).generateSecret(args.get(0).as(KeySpec.class))); + }}); // Sun put("sun/misc/SharedSecrets", new HashMap, Context, Object>>() {{ put("getJavaLangAccess()Lsun/misc/JavaLangAccess;", (targetObject, args, context) -> null); }}); put("sun/misc/JavaLangAccess", new HashMap, Context, Object>>() {{ - put("getConstantPool(Ljava/lang/Class;)Lsun/reflect/ConstantPool;", (targetObject, args, context) -> new JavaConstantPool(args.get(0).as(JavaClass.class))); + put("getConstantPool(Ljava/lang/Class;)Lsun/reflect/ConstantPool;", (targetObject, args, context) -> new JavaConstantPool(context.constantPools.get(args.get(0).as(JavaClass.class).getClassNode()))); }}); put("sun/reflect/ConstantPool", new HashMap, Context, Object>>() {{ put("getSize()I", (targetObject, args, context) -> targetObject.as(JavaConstantPool.class).getSize()); @@ -557,35 +899,33 @@ public boolean canCheckInstanceOf(JavaValue target, Type type, Context context) } private static void initObject(Context context, String className, JavaValue object) { - WrappedClassNode wrappedClassNode = context.dictionary.get(className); - if (wrappedClassNode != null) { - ClassNode classNode = wrappedClassNode.classNode; - - for (FieldNode field : classNode.fields) { + ClassNode classNode = context.dictionary.get(className); + if (classNode != null) { + for (FieldNode field : classNode.fields) { switch (field.desc) { case "B": - context.provider.setField(classNode.name, field.name, field.desc, object, (byte) 0, context); + context.provider.setField(classNode.name, field.name, field.desc, object, (byte) 0, context); break; case "S": - context.provider.setField(classNode.name, field.name, field.desc, object, (short) 0, context); + context.provider.setField(classNode.name, field.name, field.desc, object, (short) 0, context); break; case "I": - context.provider.setField(classNode.name, field.name, field.desc, object, 0, context); + context.provider.setField(classNode.name, field.name, field.desc, object, 0, context); break; case "J": - context.provider.setField(classNode.name, field.name, field.desc, object, 0L, context); + context.provider.setField(classNode.name, field.name, field.desc, object, 0L, context); break; case "F": - context.provider.setField(classNode.name, field.name, field.desc, object, 0.0, context); + context.provider.setField(classNode.name, field.name, field.desc, object, 0.0, context); break; case "D": - context.provider.setField(classNode.name, field.name, field.desc, object, 0.0D, context); + context.provider.setField(classNode.name, field.name, field.desc, object, 0.0D, context); break; case "C": - context.provider.setField(classNode.name, field.name, field.desc, object, (char) 0, context); + context.provider.setField(classNode.name, field.name, field.desc, object, (char) 0, context); break; case "Z": - context.provider.setField(classNode.name, field.name, field.desc, object, false, context); + context.provider.setField(classNode.name, field.name, field.desc, object, false, context); break; } } @@ -615,6 +955,8 @@ public boolean canCheckEquality(JavaValue first, JavaValue second, Context conte } private static JavaClass[] toJavaClass(Object[] arr) { + if(arr == null) + return new JavaClass[0]; JavaClass[] clazz = new JavaClass[arr.length]; for (int i = 0; i < arr.length; i++) { clazz[i] = (JavaClass) arr[i]; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/MappedMethodProvider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/MappedMethodProvider.java index e0005534..bd5864ce 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/MappedMethodProvider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/MappedMethodProvider.java @@ -21,39 +21,37 @@ import com.javadeobfuscator.deobfuscator.executor.providers.MethodProvider; import com.javadeobfuscator.deobfuscator.executor.values.JavaObject; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.ClassNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; import java.util.*; public class MappedMethodProvider extends MethodProvider { - private Map classpath = new HashMap<>(); + private Map classpath = new HashMap<>(); - public MappedMethodProvider(Map classpath) { + public MappedMethodProvider(Map classpath) { this.classpath = classpath; } public boolean canInvokeMethod(String className, String methodName, String methodDesc, JavaValue targetObject, List args, Context context) { - WrappedClassNode wrappedClassNode = classpath.get(className); - if(wrappedClassNode == null) + ClassNode classNode = classpath.get(className); + if(classNode == null) return false; - MethodNode methodNode = wrappedClassNode.classNode.methods.stream().filter(mn -> mn.name.equals(methodName) && mn.desc.equals(methodDesc)).findFirst().orElse(null); + MethodNode methodNode = classNode.methods.stream().filter(mn -> mn.name.equals(methodName) && mn.desc.equals(methodDesc)).findFirst().orElse(null); return methodNode != null; } @Override public Object invokeMethod(String className, String methodName, String methodDesc, JavaValue targetObject, List args, Context context) { - WrappedClassNode wrappedClassNode = classpath.get(className); - if (wrappedClassNode != null) { - ClassNode classNode = wrappedClassNode.classNode; + ClassNode classNode = classpath.get(className); + if (classNode != null) { MethodNode methodNode = classNode.methods.stream().filter(mn -> mn.name.equals(methodName) && mn.desc.equals(methodDesc)).findFirst().orElse(null); if (methodNode != null) { List argsClone = new ArrayList<>(); for (JavaValue arg : args) { argsClone.add(arg.copy()); } - return MethodExecutor.execute(wrappedClassNode, methodNode, argsClone, targetObject == null ? new JavaObject(null, "java/lang/Object") : targetObject, context); + return MethodExecutor.execute(classNode, methodNode, argsClone, targetObject == null ? new JavaObject(null, "java/lang/Object") : targetObject, context); } throw new IllegalArgumentException("Could not find method " + methodName + methodDesc); } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/ReflectiveProvider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/ReflectiveProvider.java index aa34df6c..c6aa7ce4 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/ReflectiveProvider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/ReflectiveProvider.java @@ -20,9 +20,9 @@ import com.javadeobfuscator.deobfuscator.executor.exceptions.ExecutionException; import com.javadeobfuscator.deobfuscator.executor.providers.Provider; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; +import org.objectweb.asm.Type; import com.javadeobfuscator.deobfuscator.utils.Utils; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; +import org.objectweb.asm.tree.ClassNode; import sun.invoke.util.BytecodeDescriptor; import java.lang.reflect.Constructor; @@ -37,12 +37,12 @@ * Not recommended for production usage */ public class ReflectiveProvider implements Provider { - private Map dictionary; + private Map dictionary; private Map methodCache = new HashMap<>(); private Map> ctorCache = new HashMap<>(); private Map fieldCache = new HashMap<>(); - public ReflectiveProvider(Map dictionary) { + public ReflectiveProvider(Map dictionary) { this.dictionary = dictionary; } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaClass.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaClass.java index 112087c5..170a6e15 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaClass.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaClass.java @@ -22,21 +22,22 @@ import com.javadeobfuscator.deobfuscator.exceptions.NoClassInPathException; import com.javadeobfuscator.deobfuscator.executor.Context; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.ClassNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.FieldNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; +import com.javadeobfuscator.deobfuscator.executor.providers.Provider; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; import com.javadeobfuscator.deobfuscator.utils.ClassTree; import com.javadeobfuscator.deobfuscator.utils.PrimitiveUtils; import com.javadeobfuscator.deobfuscator.utils.Utils; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; public class JavaClass { + private static final WeakHashMap> notFound = new WeakHashMap<>(); + private final String name; private final Type type; - private final WrappedClassNode wrappedClassNode; private final ClassNode classNode; private final Context context; private final boolean isPrimitive; @@ -57,21 +58,24 @@ public JavaClass(String name, Context context) { } primitive = PrimitiveUtils.getPrimitiveByName(elementType.getClassName()); if (primitive == null) { - this.wrappedClassNode = context.dictionary.get(elementType.getInternalName()); - if (this.wrappedClassNode == null) { - System.out.println("Could not find classnode " + this.name); + this.classNode = context.dictionary.get(elementType.getInternalName()); + if (this.classNode == null) { + notFound.compute(context.provider, (ctx, set) -> { + if (set == null) set = new HashSet<>(); + if (set.add(internalName)) { + System.out.println("Could not find classnode " + this.name); + } + return set; + }); throw new NoClassInPathException(this.name); } - this.classNode = this.wrappedClassNode.classNode; this.isPrimitive = false; } else { - this.wrappedClassNode = context.dictionary.get("java/lang/Object"); - this.classNode = this.wrappedClassNode.classNode; + this.classNode = context.dictionary.get("java/lang/Object"); this.isPrimitive = false; } } else { this.type = Type.getType(primitive); - this.wrappedClassNode = null; this.classNode = null; this.isPrimitive = true; } @@ -88,20 +92,19 @@ public String getSimpleName() { private Map hierachy = new HashMap<>(); public ClassNode assureLoaded(String ref) { - WrappedClassNode clazz = context.dictionary.get(ref); + ClassNode clazz = context.dictionary.get(ref); if (clazz == null) { throw new IllegalArgumentException("No class in path " + ref); } - return clazz.classNode; + return clazz; } public ClassTree getClassTree(String classNode) { ClassTree tree = hierachy.get(classNode); if (tree == null) { - tree = new ClassTree(); - tree.thisClass = classNode; + tree = new ClassTree(classNode); hierachy.put(classNode, tree); - loadHierachy(context.dictionary.get(classNode).classNode); + loadHierachy(context.dictionary.get(classNode)); } return tree; } @@ -165,9 +168,9 @@ private boolean isAssignableFrom(String type1, String type2) { return false; } - public JavaMethod getDeclaredMethod(String name, JavaClass[] clazz) { + public JavaMethod getDeclaredMethod(String name, JavaClass[] params) { StringBuilder descBuilder = new StringBuilder("("); - for (JavaClass javaClass : clazz) { + for (JavaClass javaClass : params) { descBuilder.append(javaClass.type.getDescriptor()); } descBuilder.append(")"); @@ -199,38 +202,73 @@ public JavaMethod getDeclaredMethod(String name, JavaClass[] clazz) { } } - public JavaMethod getMethod(String name, JavaClass[] clazz) { + public JavaMethod getMethod(String name, JavaClass[] params) { StringBuilder descBuilder = new StringBuilder("("); - for (JavaClass javaClass : clazz) { + for (JavaClass javaClass : params) { descBuilder.append(javaClass.type.getDescriptor()); } descBuilder.append(")"); String desc = descBuilder.toString(); - List possibleMethods = new ArrayList<>(); - ClassNode node = this.classNode; + JavaClass clazz = this; while(true) { - for (MethodNode methodNode : node.methods) { + List possibleMethods = new ArrayList<>(); + for (MethodNode methodNode : clazz.classNode.methods) { if(!methodNode.name.startsWith("<") && Modifier.isPublic(methodNode.access)) possibleMethods.add(methodNode); } - if(node.superName == null) + possibleMethods = possibleMethods.stream().filter(methodNode -> methodNode.name.equals(name) && methodNode.desc.startsWith(desc)).collect(Collectors.toList()); + if (possibleMethods.size() > 1) { + List returnTypes = new ArrayList<>(); + for (MethodNode m : possibleMethods) { + returnTypes.add(Type.getReturnType(m.desc)); + } + Collections.sort(returnTypes, (o1, o2) -> { + String s1 = o1.getInternalName(); + String s2 = o2.getInternalName(); + if (isAssignableFrom(s1, s2)) { + return 1; + } else if (isAssignableFrom(s2, s1)) { + return -1; + } + return 0; + }); + MethodNode target = possibleMethods.stream().filter(mn -> mn.desc.endsWith(returnTypes.get(0).getDescriptor())).findFirst().orElse(null); + return new JavaMethod(clazz, target); + } else if (possibleMethods.size() == 1) { + return new JavaMethod(clazz, possibleMethods.get(0)); + } + if(clazz.classNode.superName == null) break; - node = new JavaClass(node.superName, context).classNode; - } - possibleMethods = possibleMethods.stream().filter(methodNode -> methodNode.name.equals(name) && methodNode.desc.startsWith(desc)).collect(Collectors.toList()); - //Removes abstract methods - Iterator itr = possibleMethods.iterator(); - while(itr.hasNext()) - { - if(Modifier.isAbstract(itr.next().access)) - itr.remove(); + clazz = new JavaClass(clazz.classNode.superName, context); + } + for(String superItf : classNode.interfaces) + { + JavaMethod method = new JavaClass(superItf, context).getMethod0(name, params); + if(method != null) + return method; + } + Utils.sneakyThrow(new NoSuchMethodException(this.name + " " + name + desc)); + return null; + } + + private JavaMethod getMethod0(String name, JavaClass[] params) + { + StringBuilder descBuilder = new StringBuilder("("); + for (JavaClass javaClass : params) { + descBuilder.append(javaClass.type.getDescriptor()); } - if (possibleMethods.size() == 0) { - Utils.sneakyThrow(new NoSuchMethodException(this.name + " " + name + desc)); - return null; - } else if (possibleMethods.size() > 1) { + descBuilder.append(")"); + String desc = descBuilder.toString(); + + List possibleMethods = new ArrayList<>(); + for (MethodNode methodNode : this.classNode.methods) + if(!methodNode.name.startsWith("<") && Modifier.isPublic(methodNode.access) + && !Modifier.isStatic(methodNode.access)) + possibleMethods.add(methodNode); + possibleMethods = possibleMethods.stream().filter(methodNode -> methodNode.name.equals(name) && methodNode.desc.startsWith(desc)).collect(Collectors.toList()); + if (possibleMethods.size() > 1) { List returnTypes = new ArrayList<>(); for (MethodNode m : possibleMethods) { returnTypes.add(Type.getReturnType(m.desc)); @@ -247,9 +285,16 @@ public JavaMethod getMethod(String name, JavaClass[] clazz) { }); MethodNode target = possibleMethods.stream().filter(mn -> mn.desc.endsWith(returnTypes.get(0).getDescriptor())).findFirst().orElse(null); return new JavaMethod(this, target); - } else { + } else if (possibleMethods.size() == 1) { return new JavaMethod(this, possibleMethods.get(0)); } + for(String superItf : classNode.interfaces) + { + JavaMethod method = new JavaClass(superItf, context).getMethod0(name, params); + if(method != null) + return method; + } + return null; } public Type getType() { @@ -272,20 +317,52 @@ public JavaMethod[] getDeclaredMethods() { public JavaMethod[] getMethods() { List methods = new ArrayList<>(); - ClassNode node = this.classNode; + JavaClass clazz = this; while(true) { - for (MethodNode methodNode : node.methods) { - if(!methodNode.name.startsWith("<") && Modifier.isPublic(methodNode.access)) - methods.add(new JavaMethod(this, methodNode)); - } - if(node.superName == null) - break; - node = new JavaClass(node.superName, context).classNode; - } + for (MethodNode methodNode : clazz.classNode.methods) { + if(!methodNode.name.startsWith("<") && Modifier.isPublic(methodNode.access)) + { + boolean duplicate = false; + for(JavaMethod jm : methods) + if(jm.getName().equals(methodNode.name) && jm.getDesc().equals(methodNode.desc)) + { + duplicate = true; + break; + } + if(!duplicate) + methods.add(new JavaMethod(clazz, methodNode)); + } + } + if(clazz.classNode.superName == null) + break; + clazz = new JavaClass(clazz.classNode.superName, context); + } + for(String superItf : classNode.interfaces) + new JavaClass(superItf, context).getMethods0(methods); return methods.toArray(new JavaMethod[methods.size()]); } + private void getMethods0(List methods) + { + for (MethodNode methodNode : this.classNode.methods) + if(!methodNode.name.startsWith("<") && Modifier.isPublic(methodNode.access) + && !Modifier.isStatic(methodNode.access)) + { + boolean duplicate = false; + for(JavaMethod jm : methods) + if(jm.getName().equals(methodNode.name) && jm.getDesc().equals(methodNode.desc)) + { + duplicate = true; + break; + } + if(!duplicate) + methods.add(new JavaMethod(this, methodNode)); + } + for(String superItf : classNode.interfaces) + new JavaClass(superItf, context).getMethods0(methods); + } + public JavaConstructor[] getDeclaredConstructors() { List methods = new ArrayList<>(); @@ -298,16 +375,9 @@ public JavaConstructor[] getDeclaredConstructors() public JavaConstructor[] getConstructors() { List methods = new ArrayList<>(); - ClassNode node = this.classNode; - while(true) - { - for(MethodNode methodNode : classNode.methods) - if(methodNode.name.equals("") && Modifier.isPublic(methodNode.access)) - methods.add(new JavaConstructor(this, methodNode.desc)); - if(node.superName == null) - break; - node = new JavaClass(node.superName, context).classNode; - } + for(MethodNode methodNode : classNode.methods) + if(methodNode.name.equals("") && Modifier.isPublic(methodNode.access)) + methods.add(new JavaConstructor(this, methodNode.desc)); return methods.toArray(new JavaConstructor[methods.size()]); } @@ -356,20 +426,36 @@ public JavaField[] getDeclaredFields() { public JavaField[] getFields() { List fields = new ArrayList<>(); - ClassNode node = this.classNode; + for (FieldNode fieldNode : classNode.fields) { + if(Modifier.isPublic(fieldNode.access)) + fields.add(new JavaField(this, fieldNode)); + } + for(String superItf : classNode.interfaces) + new JavaClass(superItf, context).getFields0(fields); + JavaClass clazz = this; while(true) { - for (FieldNode fieldNode : node.fields) { - if(Modifier.isPublic(fieldNode.access)) - fields.add(new JavaField(this, fieldNode)); - } - if(node.superName == null) - break; - node = new JavaClass(node.superName, context).classNode; + if(clazz.classNode.superName == null) + break; + clazz = new JavaClass(clazz.classNode.superName, context); + for (FieldNode fieldNode : clazz.classNode.fields) { + if(Modifier.isPublic(fieldNode.access)) + fields.add(new JavaField(clazz, fieldNode)); + } } return fields.toArray(new JavaField[fields.size()]); } + private void getFields0(List fields) + { + for (FieldNode fieldNode : this.classNode.fields) { + if(Modifier.isPublic(fieldNode.access)) + fields.add(new JavaField(this, fieldNode)); + } + for(String superItf : classNode.interfaces) + new JavaClass(superItf, context).getFields0(fields); + } + public JavaField getDeclaredField(String fieldName) { for (FieldNode fieldNode : this.classNode.fields) { if (fieldNode.name.equals(fieldName)) @@ -380,21 +466,44 @@ public JavaField getDeclaredField(String fieldName) { } public JavaField getField(String fieldName) { - ClassNode node = this.classNode; + for (FieldNode fieldNode : this.classNode.fields) { + if(Modifier.isPublic(fieldNode.access) && fieldNode.name.equals(fieldName)) + return new JavaField(this, fieldNode); + } + for(String superItf : classNode.interfaces) { + JavaField field = new JavaClass(superItf, context).getField0(fieldName); + if(field != null) + return field; + } + JavaClass clazz = this; while(true) { - for (FieldNode fieldNode : node.fields) { + if(clazz.classNode.superName == null) + break; + clazz = new JavaClass(clazz.classNode.superName, context); + for (FieldNode fieldNode : clazz.classNode.fields) { if(Modifier.isPublic(fieldNode.access) && fieldNode.name.equals(fieldName)) - return new JavaField(this, fieldNode); + return new JavaField(clazz, fieldNode); } - if(node.superName == null) - break; - node = new JavaClass(node.superName, context).classNode; } Utils.sneakyThrow(new NoSuchFieldException(fieldName)); return null; } + private JavaField getField0(String fieldName) + { + for (FieldNode fieldNode : this.classNode.fields) { + if(Modifier.isPublic(fieldNode.access) && fieldNode.name.equals(fieldName)) + return new JavaField(this, fieldNode); + } + for(String superItf : classNode.interfaces) { + JavaField field = new JavaClass(superItf, context).getField0(fieldName); + if(field != null) + return field; + } + return null; + } + public JavaClass getSuperclass() { if (this.type.getSort() == Type.ARRAY) { return new JavaClass("java/lang/Object", this.context); @@ -469,10 +578,6 @@ public ClassNode getClassNode() { return this.classNode; } - public WrappedClassNode getWrappedClassNode() { - return this.wrappedClassNode; - } - public boolean isInterface() { return !((classNode.access & Opcodes.ACC_INTERFACE) == 0); } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaConstantPool.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaConstantPool.java index 2d3abef8..c1aadf8e 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaConstantPool.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaConstantPool.java @@ -16,14 +16,16 @@ package com.javadeobfuscator.deobfuscator.executor.defined.types; +import com.javadeobfuscator.deobfuscator.asm.ConstantPool; + public class JavaConstantPool { - private final JavaClass clazz; + private final ConstantPool constantPool; - public JavaConstantPool(JavaClass javaClass) { - this.clazz = javaClass; + public JavaConstantPool(ConstantPool constantPool) { + this.constantPool = constantPool; } public int getSize() { - return clazz.getWrappedClassNode().constantPoolSize; + return this.constantPool.getSize(); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaConstructor.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaConstructor.java index 836d90c3..c6f1aa4a 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaConstructor.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaConstructor.java @@ -1,14 +1,10 @@ package com.javadeobfuscator.deobfuscator.executor.defined.types; import com.javadeobfuscator.deobfuscator.executor.Context; -import com.javadeobfuscator.deobfuscator.executor.values.JavaDouble; -import com.javadeobfuscator.deobfuscator.executor.values.JavaFloat; -import com.javadeobfuscator.deobfuscator.executor.values.JavaInteger; -import com.javadeobfuscator.deobfuscator.executor.values.JavaLong; -import com.javadeobfuscator.deobfuscator.executor.values.JavaObject; -import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; +import com.javadeobfuscator.deobfuscator.executor.values.*; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.MethodNode; import com.javadeobfuscator.deobfuscator.utils.PrimitiveUtils; import java.util.ArrayList; @@ -40,19 +36,39 @@ public JavaClass[] getParameterTypes() { return params.toArray(new JavaClass[params.size()]); } - public Object newInstance(Context context, Object[] args) + public Object newInstance(Context context, JavaValue argsObject) { + Object[] args; + String[] argTypes; + if(argsObject.value() == null) + { + args = null; + argTypes = null; + }else + { + args = (Object[])((JavaArray)argsObject).value(); + argTypes = ((JavaArray)argsObject).getTypeArray(); + } MethodNode method = clazz.getClassNode().methods.stream().filter(m -> m.name.equals("") && m.desc.equals(desc)).findFirst().orElse(null); List javaArgs = new ArrayList<>(); - for(Object arg : args) + for(int i = 0; i < args.length; i++) { + Object arg = args[i]; if(arg instanceof Type) { Type type = (Type)arg; arg = new JavaClass(type.getInternalName().replace('/', '.'), context); } - if(arg instanceof Integer) + if(arg instanceof Boolean) + javaArgs.add(0, new JavaBoolean((Boolean)arg)); + else if(arg instanceof Character) + javaArgs.add(0, new JavaCharacter((Character)arg)); + else if(arg instanceof Byte) + javaArgs.add(0, new JavaByte((Byte)arg)); + else if(arg instanceof Short) + javaArgs.add(0, new JavaShort((Short)arg)); + else if(arg instanceof Integer) javaArgs.add(0, new JavaInteger((Integer)arg)); else if(arg instanceof Float) javaArgs.add(0, new JavaFloat((Float)arg)); @@ -60,12 +76,10 @@ else if(arg instanceof Double) javaArgs.add(0, new JavaDouble((Double)arg)); else if(arg instanceof Long) javaArgs.add(0, new JavaLong((Long)arg)); - else if(arg instanceof String) - javaArgs.add(0, new JavaObject(arg, "java/lang/String")); - else if(arg instanceof JavaClass) - javaArgs.add(0, new JavaObject(arg, "java/lang/Class")); + else if(arg != null && arg.getClass().isArray()) + javaArgs.add(new JavaArray(arg)); else - javaArgs.add(JavaValue.valueOf(arg)); + javaArgs.add(new JavaObject(arg, argTypes[i])); } JavaObject instance = new JavaObject(clazz.getName().replace(".", "/")); context.provider.invokeMethod(clazz.getName().replace(".", "/"), method.name, method.desc, diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaField.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaField.java index 2baefa4b..2a39cb0f 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaField.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaField.java @@ -16,8 +16,8 @@ package com.javadeobfuscator.deobfuscator.executor.defined.types; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.FieldNode; import com.javadeobfuscator.deobfuscator.utils.PrimitiveUtils; public class JavaField { diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/utils/WrappedClassNode.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaFieldHandle.java similarity index 55% rename from src/main/java/com/javadeobfuscator/deobfuscator/utils/WrappedClassNode.java rename to src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaFieldHandle.java index fbd2bf47..545636ac 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/utils/WrappedClassNode.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaFieldHandle.java @@ -14,20 +14,20 @@ * limitations under the License. */ -package com.javadeobfuscator.deobfuscator.utils; +package com.javadeobfuscator.deobfuscator.executor.defined.types; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.ClassNode; +public class JavaFieldHandle extends JavaHandle { + public final String clazz; + public final String name; + public final String desc; + public final String type; + public final boolean setter; -public class WrappedClassNode { - public ClassNode classNode; - public int constantPoolSize; - - public WrappedClassNode(ClassNode classNode, int constantPoolSize) { - this.classNode = classNode; - this.constantPoolSize = constantPoolSize; - } - - public ClassNode getClassNode() { - return this.classNode; + public JavaFieldHandle(String clazz, String name, String desc, String type, boolean setter) { + this.clazz = clazz; + this.name = name; + this.desc = desc; + this.type = type; + this.setter = setter; } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaHandle.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaHandle.java new file mode 100644 index 00000000..61451604 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaHandle.java @@ -0,0 +1,6 @@ +package com.javadeobfuscator.deobfuscator.executor.defined.types; + +public class JavaHandle +{ + +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethod.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethod.java index 8b4e2810..702c266d 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethod.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethod.java @@ -16,24 +16,16 @@ package com.javadeobfuscator.deobfuscator.executor.defined.types; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import java.security.cert.Certificate; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - +import com.google.common.primitives.Primitives; +import com.javadeobfuscator.deobfuscator.executor.Context; import com.javadeobfuscator.deobfuscator.executor.exceptions.ExecutionException; -import com.javadeobfuscator.deobfuscator.executor.values.JavaObject; -import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; +import com.javadeobfuscator.deobfuscator.executor.values.*; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.MethodNode; import com.javadeobfuscator.deobfuscator.utils.PrimitiveUtils; public class JavaMethod { @@ -91,21 +83,90 @@ public int hashCode() { return getDeclaringClass().getName().hashCode() ^ getName().hashCode(); } - public Object invoke(JavaValue instance, Object[] args) { + public Object invoke(JavaValue instance, JavaValue argsObject, Context context) { + Object[] args; + String[] argTypes; + if(argsObject.value() == null) + { + args = null; + argTypes = null; + }else + { + args = (Object[])((JavaArray)argsObject).value(); + argTypes = ((JavaArray)argsObject).getTypeArray(); + } try { + //Fix for unboxing/boxing + List fixed = new ArrayList<>(); + if(args != null && args.length == getParameterTypes().length) + for(int i = 0; i < args.length; i++) + { + Object o = args[i]; + JavaClass[] params = getParameterTypes(); + JavaClass argClass = params[i]; + if(argClass.isPrimitive()) + { + Object value = o; + if(Primitives.unwrap(value.getClass()).getName().equals(argClass.getName())) + switch(argClass.getName()) + { + case "boolean": + args[i] = new JavaBoolean((Boolean)o); + fixed.add(i); + break; + case "byte": + args[i] = new JavaByte((Byte)o); + fixed.add(i); + break; + case "char": + args[i] = new JavaCharacter((Character)o); + fixed.add(i); + break; + case "double": + args[i] = new JavaDouble((Double)o); + fixed.add(i); + break; + case "float": + args[i] = new JavaFloat((Float)o); + fixed.add(i); + break; + case "int": + args[i] = new JavaInteger((Integer)o); + fixed.add(i); + break; + case "long": + args[i] = new JavaLong((Long)o); + fixed.add(i); + break; + case "short": + args[i] = new JavaShort((Short)o); + fixed.add(i); + break; + } + } + } List argsobjects = new ArrayList<>(); if (args != null) { - for (Object o : args) { - if(!(o instanceof JavaValue)) - //Arrays - argsobjects.add(JavaValue.valueOf(o)); - else - argsobjects.add((JavaValue) o); + for (int i = 0; i < args.length; i++) { + Object o = args[i]; + if(!fixed.contains(i)) + { + if(o != null && o.getClass().isArray()) + argsobjects.add(new JavaArray(o)); + else + argsobjects.add(new JavaObject(o, argTypes[i])); + }else + argsobjects.add((JavaValue)o); } } - if (this.clazz.getContext().provider.canInvokeMethod(this.clazz.getWrappedClassNode().classNode.name, this.method.name, this.method.desc, instance, argsobjects, this.clazz.getContext())) { - return this.clazz.getContext().provider.invokeMethod(this.clazz.getWrappedClassNode().classNode.name, this.method.name, this.method.desc, instance, argsobjects, this.clazz.getContext()); + if (context.provider.canInvokeMethod(this.clazz.getClassNode().name, this.method.name, this.method.desc, instance, argsobjects, context)) { + Object o = this.clazz.getContext().provider.invokeMethod(this.clazz.getClassNode().name, this.method.name, this.method.desc, instance, argsobjects, context); + context.pop(); + context.pop(); + context.pop(); + context.pop(); + return o; } } catch (ExecutionException ex) { throw ex; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethodHandle.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethodHandle.java index 327a0855..aa9a40be 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethodHandle.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethodHandle.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.executor.defined.types; -public class JavaMethodHandle { +public class JavaMethodHandle extends JavaHandle { public final String clazz; public final String name; public final String desc; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaThread.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaThread.java index 789e5817..a3833675 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaThread.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaThread.java @@ -2,11 +2,9 @@ import com.javadeobfuscator.deobfuscator.executor.Context; import com.javadeobfuscator.deobfuscator.executor.MethodExecutor; -import com.javadeobfuscator.deobfuscator.executor.ThreadStore; import com.javadeobfuscator.deobfuscator.executor.values.JavaObject; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.ClassNode; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode; -import com.javadeobfuscator.deobfuscator.utils.WrappedClassNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; import java.util.Collections; @@ -42,17 +40,14 @@ public JavaObject getInstance() { public void start() { if (!started) { started = true; - WrappedClassNode wrappedClass = context.dictionary.get(instance.type()); - if (wrappedClass != null) { - ClassNode classNode = wrappedClass.classNode; + ClassNode classNode = context.dictionary.get(instance.type()); + if (classNode != null) { MethodNode method = classNode.methods.stream().filter(mn -> mn.name.equals("run") && mn.desc.equals("()V")).findFirst().orElse(null); if (method != null) { - Context threadContext = new Context(context.provider); - threadContext.dictionary = context.dictionary; - threadContext.file = context.file; + Context threadContext = context.copyForNewThread(); - thread = new Thread(() -> MethodExecutor.execute(wrappedClass, method, Collections.emptyList(), instance, threadContext)); - ThreadStore.addThread(thread.getId(), this); + thread = new Thread(() -> MethodExecutor.execute(classNode, method, Collections.emptyList(), instance, threadContext)); + context.threadStore.addThread(thread.getId(), this); this.context = threadContext; thread.start(); diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/exceptions/NoSuchMethodHandlerException.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/exceptions/NoSuchMethodHandlerException.java index ad55f1c0..0e108e8e 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/exceptions/NoSuchMethodHandlerException.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/exceptions/NoSuchMethodHandlerException.java @@ -17,7 +17,18 @@ package com.javadeobfuscator.deobfuscator.executor.exceptions; public class NoSuchMethodHandlerException extends NoSuchHandlerException { + private boolean isThrownFromInvoke; + public NoSuchMethodHandlerException(String msg) { super(msg); } + + public NoSuchMethodHandlerException setThrownFromInvoke(boolean isThrownFromExe) { + this.isThrownFromInvoke = isThrownFromExe; + return this; + } + + public boolean isThrownFromInvoke() { + return isThrownFromInvoke; + } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/DelegatingProvider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/DelegatingProvider.java index 7c0b00ba..3ab3d55d 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/DelegatingProvider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/DelegatingProvider.java @@ -22,7 +22,7 @@ import com.javadeobfuscator.deobfuscator.executor.Context; import com.javadeobfuscator.deobfuscator.executor.exceptions.ExecutionException; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; +import org.objectweb.asm.Type; public class DelegatingProvider implements Provider { diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/FieldProvider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/FieldProvider.java index 4925aa5e..a08794b2 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/FieldProvider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/FieldProvider.java @@ -20,7 +20,7 @@ import com.javadeobfuscator.deobfuscator.executor.Context; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; +import org.objectweb.asm.Type; public abstract class FieldProvider implements Provider { @Override diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/MethodProvider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/MethodProvider.java index 4b50123a..966c25f0 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/MethodProvider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/MethodProvider.java @@ -18,7 +18,7 @@ import com.javadeobfuscator.deobfuscator.executor.Context; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; +import org.objectweb.asm.Type; public abstract class MethodProvider implements Provider { diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/Provider.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/Provider.java index 6d0688d8..d808ec1f 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/Provider.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/providers/Provider.java @@ -20,7 +20,7 @@ import com.javadeobfuscator.deobfuscator.executor.Context; import com.javadeobfuscator.deobfuscator.executor.values.JavaValue; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; +import org.objectweb.asm.Type; public interface Provider { diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaAddress.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaAddress.java index e83d2fbd..04b00315 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaAddress.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaAddress.java @@ -16,7 +16,8 @@ package com.javadeobfuscator.deobfuscator.executor.values; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.AbstractInsnNode; +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; public class JavaAddress extends JavaValue { @@ -36,6 +37,6 @@ public Object value() { } public String toString() { - return "JavaAddress(addr=" + value.index + ")"; + return "JavaAddress(addr=" + Utils.prettyprint(this.value) + ")"; } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaArray.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaArray.java new file mode 100644 index 00000000..2ea9270d --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaArray.java @@ -0,0 +1,99 @@ +/* + * Copyright 2016 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.executor.values; + +import java.lang.reflect.Array; +import java.util.AbstractMap; +import java.util.Map.Entry; + +import org.objectweb.asm.Type; + +public class JavaArray extends JavaObject { + private Object array; + private String[] typeArray; + + public JavaArray(Object array) + { + super(array, "java/lang/Object"); + if(!array.getClass().isArray()) + throw new IllegalArgumentException("Object must be array"); + this.array = array; + typeArray = new String[Array.getLength(array)]; + for(int i = 0; i < Array.getLength(array); i++) + { + Object o = Array.get(array, i); + if(o == null) + typeArray[i] = "java/lang/Object"; + else if(Type.getType(o.getClass()).getSort() == Type.OBJECT) + typeArray[i] = JavaValue.valueOf(o).type(); + else if(Type.getType(o.getClass()).getSort() == Type.ARRAY) + typeArray[i] = "java/lang/Object"; + else + typeArray[i] = Type.getType(o.getClass()).getClassName(); + } + } + + public JavaArray(Object array, String[] typeArray) + { + super(array, "java/lang/Object"); + if(!array.getClass().isArray()) + throw new IllegalArgumentException("Object must be array"); + this.array = array; + this.typeArray = typeArray; + } + + + public void onValueStored(int index, String type) + { + typeArray[index] = type; + } + + public String getValueType(int index) + { + return typeArray[index]; + } + + public String[] getTypeArray() + { + return typeArray; + } + + public Entry getObjectArrayWithValues() + { + return new AbstractMap.SimpleEntry<>(array, typeArray); + } + + @Override + public Object value() { + return this.array; + } + + @Override + public JavaObject copy() { + return this; + } + + @Override + public void initialize(Object value) { + throw new IllegalStateException("Cannot initialize an array twice"); + } + + @Override + public String toString() { + return "JavaArray@" + Integer.toHexString(System.identityHashCode(this)) + "(value=" + array + ")"; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaObject.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaObject.java index 4d272563..971f7c03 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaObject.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaObject.java @@ -16,9 +16,14 @@ package com.javadeobfuscator.deobfuscator.executor.values; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + public class JavaObject extends JavaValue { private Object value; private String type; + public static final Map> patchClasses = new HashMap<>(); public JavaObject(Object value, String type) { this.value = value; @@ -39,7 +44,10 @@ public JavaObject copy() { } public void initialize(Object value) { - this.value = value; + if(patchClasses.containsKey(type)) + this.value = patchClasses.get(type).apply(value); + else + this.value = value; } public String type() { diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaValue.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaValue.java index 6d1e11ca..1eaa41ca 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaValue.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaValue.java @@ -16,8 +16,11 @@ package com.javadeobfuscator.deobfuscator.executor.values; +import org.objectweb.asm.Type; + import com.google.common.primitives.Primitives; import com.javadeobfuscator.deobfuscator.executor.MethodExecutor; +import com.javadeobfuscator.deobfuscator.executor.defined.types.*; import com.javadeobfuscator.deobfuscator.executor.exceptions.ExecutionException; public abstract class JavaValue { @@ -47,6 +50,9 @@ public Object value() { } public T as(Class clazz) { + //TODO: Fix this + if(value() instanceof JavaValue) + return ((JavaValue)value()).as(clazz); if (Primitives.unwrap(clazz) != clazz) { throw new ExecutionException("Cannot call as(Class clazz) with a primitive class"); } @@ -73,7 +79,28 @@ public String type() { } public static JavaValue valueOf(Object cst) { - return new JavaObject(cst, "java/lang/Object"); //fixme type + if(cst == null) + return new JavaObject(cst, "java/lang/Object"); + else if(cst.getClass().isArray()) + return new JavaObject(cst, "java/lang/Object"); + else if(cst instanceof JavaThread) + return new JavaObject(cst, "java/lang/Thread"); + else if(cst instanceof JavaHandle) + return new JavaObject(cst, "java/lang/invoke/MethodHandle"); + else if(cst instanceof JavaMethod) + return new JavaObject(cst, "java/lang/reflect/Method"); + else if(cst instanceof JavaField) + return new JavaObject(cst, "java/lang/reflect/Field"); + else if(cst instanceof JavaConstructor) + return new JavaObject(cst, "java/lang/reflect/Constructor"); + else if(cst instanceof JavaConstantPool) + return new JavaObject(cst, "sun/reflect/ConstantPool"); + else if(cst instanceof JavaClass) + return new JavaObject(cst, "java/lang/Class"); + else if(cst instanceof JavaObject) + return new JavaObject(cst, ((JavaObject)cst).type()); + else + return new JavaObject(cst, Type.getType(cst.getClass()).getInternalName()); } public static JavaValue forPrimitive(Class prim) { diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/graph/GraphHelper.java b/src/main/java/com/javadeobfuscator/deobfuscator/graph/GraphHelper.java new file mode 100644 index 00000000..0fbda1b8 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/graph/GraphHelper.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.graph; + +import org.objectweb.asm.tree.ClassNode; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class GraphHelper { + public static Map validateUniqueClasses(Collection nodes) { + Map map = new HashMap<>(); + for (ClassNode classNode : nodes) { + if (map.put(classNode.name, classNode) != null) { + throw new IllegalArgumentException("Duplicate class " + classNode.name); + } + } + return map; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraph.java b/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraph.java new file mode 100644 index 00000000..e7446886 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraph.java @@ -0,0 +1,92 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.graph.callgraph; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class CallGraph { + private final Map map; + private final Set entryPoints; + + CallGraph(Map callGraph, Set entryPoints) { + this.map = ImmutableMap.copyOf(callGraph); + this.entryPoints = ImmutableSet.copyOf(entryPoints); + } + + public final CallGraphNode get(ClassNode owner, MethodNode method) { + return map.get(getKey(owner, method)); + } + + public final CallGraphNode get(String owner, String name, String desc) { + return map.get(getKey(owner, name, desc)); + } + + public final Collection values() { + return map.values(); + } + + /** + * An entry point being any node with no incoming edges, not necessarily a Java entry point + */ + public final Set getEntryPoints() { + return entryPoints; + } + + static Key getKey(ClassNode classNode, MethodNode methodNode) { + return new Key(classNode.name, methodNode.name, methodNode.desc); + } + + static Key getKey(String owner, String name, String desc) { + return new Key(owner, name, desc); + } + + + static class Key { + final String owner; + final String name; + final String desc; + + public Key(String owner, String name, String desc) { + this.owner = owner; + this.name = name; + this.desc = desc; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Key key = (Key) o; + return Objects.equals(owner, key.owner) && + Objects.equals(name, key.name) && + Objects.equals(desc, key.desc); + } + + @Override + public int hashCode() { + return Objects.hash(owner, name, desc); + } + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraphBuilder.java b/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraphBuilder.java new file mode 100644 index 00000000..c4cf9573 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraphBuilder.java @@ -0,0 +1,141 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.graph.callgraph; + +import com.javadeobfuscator.deobfuscator.graph.inheritancegraph.InheritanceGraph; +import com.javadeobfuscator.deobfuscator.graph.inheritancegraph.InheritanceGraphNode; +import com.javadeobfuscator.deobfuscator.utils.TransformerHelper; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import java.util.*; + +import static com.javadeobfuscator.deobfuscator.graph.GraphHelper.validateUniqueClasses; + +public class CallGraphBuilder { + private List nodes = new ArrayList<>(); + private InheritanceGraph inheritanceGraph; + + private CallGraphBuilder() { + } + + public static CallGraphBuilder newBuilder() { + return new CallGraphBuilder(); + } + + public final CallGraphBuilder withNode(ClassNode node) { + nodes.add(node); + return this; + } + + public final CallGraphBuilder withNodes(Collection nodes) { + this.nodes.addAll(nodes); + return this; + } + + public final CallGraphBuilder withInheritanceGraph(InheritanceGraph inheritanceGraph) { + this.inheritanceGraph = inheritanceGraph; + return this; + } + + public final CallGraph build() { + Map classMap = validateUniqueClasses(nodes); + + Map callGraph = new HashMap<>(); + + // First, compute the call graph + for (ClassNode classNode : nodes) { + for (MethodNode methodNode : classNode.methods) { + CallGraphNode thisNode = callGraph.computeIfAbsent(CallGraph.getKey(classNode, methodNode), CallGraphNode::new); + + if (methodNode.instructions == null) { + continue; + } + + for (ListIterator it = methodNode.instructions.iterator(); it.hasNext(); ) { + AbstractInsnNode insn = it.next(); + + if (insn instanceof MethodInsnNode) { + MethodInsnNode methodInsnNode = (MethodInsnNode) insn; + CallGraph.Key otherKey = CallGraph.getKey(methodInsnNode.owner, methodInsnNode.name, methodInsnNode.desc); + CallGraphNode otherNode = callGraph.computeIfAbsent(otherKey, CallGraphNode::new); + + thisNode.addXrefTo(otherNode); + otherNode.addXrefFrom(thisNode); + + // Handle inheritance + if (inheritanceGraph != null) { + InheritanceGraphNode inheritanceGraphNode = inheritanceGraph.get(methodInsnNode.owner); + if (inheritanceGraphNode != null) { + for (InheritanceGraphNode child : inheritanceGraphNode.getChildren()) { + ClassNode childNode = classMap.get(child.getOwner()); + if (childNode == null) { + continue; + } + MethodNode method = TransformerHelper.findMethodNode(childNode, methodInsnNode.name, methodInsnNode.desc); + if (method == null) { + continue; + } + otherKey = CallGraph.getKey(child.getOwner(), methodInsnNode.name, methodInsnNode.desc); + otherNode = callGraph.computeIfAbsent(otherKey, CallGraphNode::new); + + thisNode.addXrefTo(otherNode); + otherNode.addXrefFrom(thisNode); + } + + for (InheritanceGraphNode parent : inheritanceGraphNode.getParents()) { + ClassNode parentNode = classMap.get(parent.getOwner()); + if (parentNode == null) { + continue; + } + MethodNode method = TransformerHelper.findMethodNode(parentNode, methodInsnNode.name, methodInsnNode.desc); + if (method == null) { + continue; + } + otherKey = CallGraph.getKey(parent.getOwner(), methodInsnNode.name, methodInsnNode.desc); + otherNode = callGraph.computeIfAbsent(otherKey, CallGraphNode::new); + + thisNode.addXrefTo(otherNode); + otherNode.addXrefFrom(thisNode); + } + } + } + } + } + } + } + + // Freeze it! + callGraph.values().forEach(CallGraphNode::freeze); + + // Next, compute the entry points + Set entryPoints = new HashSet<>(); + for (CallGraphNode node : callGraph.values()) { + if (!classMap.containsKey(node.getOwner())) { + continue; + } + + if (node.getXrefsFrom().isEmpty()) { + entryPoints.add(node); + } + } + + return new CallGraph(callGraph, entryPoints); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraphNode.java b/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraphNode.java new file mode 100644 index 00000000..b68d545a --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/graph/callgraph/CallGraphNode.java @@ -0,0 +1,97 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.graph.callgraph; + +import com.google.common.collect.ImmutableSet; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public final class CallGraphNode { + private final String owner; + private final String name; + private final String desc; + + private Set xrefsTo = new HashSet<>(); + private Set xrefsFrom = new HashSet<>(); + + private Object userData; + + CallGraphNode(CallGraph.Key key) { + this.owner = key.owner; + this.name = key.name; + this.desc = key.desc; + } + + + void addXrefTo(CallGraphNode other) { + xrefsTo.add(other); + } + + void addXrefFrom(CallGraphNode other) { + xrefsFrom.add(other); + } + + void freeze() { + xrefsTo = ImmutableSet.copyOf(xrefsTo); + xrefsFrom = ImmutableSet.copyOf(xrefsFrom); + } + + public String getOwner() { + return owner; + } + + public String getName() { + return name; + } + + public String getDesc() { + return desc; + } + + public Set getXrefsTo() { + return xrefsTo; + } + + public Set getXrefsFrom() { + return xrefsFrom; + } + + public Object getUserData() { + return userData; + } + + public void setUserData(Object userData) { + this.userData = userData; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CallGraphNode that = (CallGraphNode) o; + return Objects.equals(owner, that.owner) && + Objects.equals(name, that.name) && + Objects.equals(desc, that.desc); + } + + @Override + public int hashCode() { + return Objects.hash(owner, name, desc); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraph.java b/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraph.java new file mode 100644 index 00000000..23572a60 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraph.java @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.graph.inheritancegraph; + +import com.google.common.collect.ImmutableMap; +import org.objectweb.asm.tree.ClassNode; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +public class InheritanceGraph { + private final Map map; + + public InheritanceGraph(Map map) { + this.map = ImmutableMap.copyOf(map); + } + + public final InheritanceGraphNode get(String owner) { + return map.get(getKey(owner)); + } + + public final Collection values() { + return map.values(); + } + + static Key getKey(ClassNode classNode) { + return new Key(classNode.name); + } + + static Key getKey(String owner) { + return new Key(owner); + } + + static class Key { + final String owner; + + public Key(String owner) { + this.owner = owner; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Key key = (Key) o; + return Objects.equals(owner, key.owner); + } + + @Override + public int hashCode() { + return Objects.hash(owner); + } + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraphBuilder.java b/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraphBuilder.java new file mode 100644 index 00000000..3959aed1 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraphBuilder.java @@ -0,0 +1,81 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.graph.inheritancegraph; + +import org.objectweb.asm.tree.ClassNode; + +import java.util.*; + +import static com.javadeobfuscator.deobfuscator.graph.GraphHelper.validateUniqueClasses; + +public class InheritanceGraphBuilder { + private List nodes = new ArrayList<>(); + + private InheritanceGraphBuilder() { + } + + public static InheritanceGraphBuilder newBuilder() { + return new InheritanceGraphBuilder(); + } + + public final InheritanceGraphBuilder withNode(ClassNode node) { + nodes.add(node); + return this; + } + + public final InheritanceGraphBuilder withNodes(Collection nodes) { + this.nodes.addAll(nodes); + return this; + } + + public final InheritanceGraph build() { + validateUniqueClasses(nodes); + + Map inheritanceGraph = new HashMap<>(); + + // First, compute the inheritance graph + for (ClassNode classNode : nodes) { + InheritanceGraphNode thisNode = inheritanceGraph.computeIfAbsent(InheritanceGraph.getKey(classNode), InheritanceGraphNode::new); + { + if (classNode.superName != null) { // In case we reached java/lang/Object + InheritanceGraphNode superClass = inheritanceGraph.computeIfAbsent(InheritanceGraph.getKey(classNode.superName), InheritanceGraphNode::new); + + thisNode.addParent(superClass); + superClass.addChild(thisNode); + } + } + { + if (classNode.interfaces != null) { + for (String intf : classNode.interfaces) { + InheritanceGraphNode intfClass = inheritanceGraph.computeIfAbsent(InheritanceGraph.getKey(intf), InheritanceGraphNode::new); + + thisNode.addParent(intfClass); + intfClass.addChild(thisNode); + } + } + } + } + + // Now, aggregate results to form an "all children" and "all parents" view + inheritanceGraph.values().forEach(InheritanceGraphNode::computeIndirectRelationships); + + // Freeze it! + inheritanceGraph.values().forEach(InheritanceGraphNode::freeze); + + return new InheritanceGraph(inheritanceGraph); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraphNode.java b/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraphNode.java new file mode 100644 index 00000000..5c07e85b --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/graph/inheritancegraph/InheritanceGraphNode.java @@ -0,0 +1,111 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.graph.inheritancegraph; + +import com.google.common.collect.ImmutableSet; + +import java.util.ArrayDeque; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public final class InheritanceGraphNode { + private final String owner; + + private Set directChildren = new HashSet<>(); + private Set directParents = new HashSet<>(); + private Set children = new HashSet<>(); + private Set parents = new HashSet<>(); + + private Object userData; + + InheritanceGraphNode(InheritanceGraph.Key key) { + this.owner = key.owner; + } + + void addChild(InheritanceGraphNode other) { + directChildren.add(other); + } + + void addParent(InheritanceGraphNode other) { + directParents.add(other); + } + + void computeIndirectRelationships() { + ArrayDeque toVisit = new ArrayDeque<>(); + + toVisit.addAll(directChildren); + while (!toVisit.isEmpty()) { + InheritanceGraphNode child = toVisit.pop(); + children.add(child); + toVisit.addAll(child.directChildren); + } + + toVisit.addAll(directParents); + while (!toVisit.isEmpty()) { + InheritanceGraphNode parent = toVisit.pop(); + parents.add(parent); + toVisit.addAll(parent.directParents); + } + } + + void freeze() { + directChildren = ImmutableSet.copyOf(directChildren); + directParents = ImmutableSet.copyOf(directParents); + } + + public String getOwner() { + return owner; + } + + public Object getUserData() { + return userData; + } + + public void setUserData(Object userData) { + this.userData = userData; + } + + public Set getDirectChildren() { + return directChildren; + } + + public Set getDirectParents() { + return directParents; + } + + public Set getChildren() { + return children; + } + + public Set getParents() { + return parents; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InheritanceGraphNode that = (InheritanceGraphNode) o; + return Objects.equals(owner, that.owner); + } + + @Override + public int hashCode() { + return Objects.hash(owner); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/AnyStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/AnyStep.java new file mode 100644 index 00000000..057a63a5 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/AnyStep.java @@ -0,0 +1,19 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; + +public class AnyStep extends IterableStep { + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + return true; + } + + @Override + public String toString() { + return "AnyStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/CastedSimpleStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/CastedSimpleStep.java new file mode 100644 index 00000000..25dece9d --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/CastedSimpleStep.java @@ -0,0 +1,62 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import java.util.Arrays; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.apache.commons.lang3.Validate; +import org.objectweb.asm.tree.AbstractInsnNode; + +public class CastedSimpleStep extends IterableStep { + + private final int[] opcodes; + + public CastedSimpleStep(int... opcodes) { + this.opcodes = opcodes; + } + + public static CastedSimpleStep ofOpcodeRangeInclusive(int start, int end) { + Validate.isTrue(start < end, "start opcode %s not smaller than end opcode %s", start, end); + int[] arr = new int[end - start + 1]; + int j = 0; + for (int i = start; i <= end; i++) { + arr[j++] = i; + } + return new CastedSimpleStep(arr); + } + + public static CastedSimpleStep ofOpcodeRangeInclusive(int start, int end, int[] additional) { + Validate.isTrue(start < end, "start opcode %s not smaller than end opcode %s", start, end); + int[] arr = new int[end - start + 1 + additional.length]; + int j = 0; + for (int i = start; i <= end; i++) { + arr[j++] = i; + } + for (int i : additional) { + arr[j++] = i; + } + return new CastedSimpleStep(arr); + } + + public int[] getOpcodes() { + return opcodes; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + int ainOpcode = ain.getOpcode(); + for (int opcode : opcodes) { + if (ainOpcode == opcode) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "SimpleStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + ", opcodes=" + Arrays.toString(this.opcodes) + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/ConstantIntInsnStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/ConstantIntInsnStep.java new file mode 100644 index 00000000..03790ffd --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/ConstantIntInsnStep.java @@ -0,0 +1,22 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; + +public class ConstantIntInsnStep extends IterableStep { + + private final Integer expectedConst; + + public ConstantIntInsnStep() { + expectedConst = null; + } + + public ConstantIntInsnStep(int expectedConst) { + this.expectedConst = expectedConst; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + return Utils.isInteger(ain) && (expectedConst == null || Utils.getIntValue(ain) == expectedConst); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/FieldInsnStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/FieldInsnStep.java new file mode 100644 index 00000000..3f939808 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/FieldInsnStep.java @@ -0,0 +1,70 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.apache.commons.lang3.Validate; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; + +public class FieldInsnStep extends IterableStep { + + private final int opcode; + private final String owner; + private final String name; + private final String desc; + + public FieldInsnStep(int opcode, String owner, String name, String desc) { + Validate.inclusiveBetween(GETSTATIC, PUTFIELD, opcode, "Invalid FieldInsn opcode %s", opcode); + this.opcode = opcode; + this.owner = owner; + this.name = name; + this.desc = desc; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + if (ain.getOpcode() != opcode) { + return false; + } + if (!(ain instanceof FieldInsnNode)) { + return false; + } + FieldInsnNode fin = (FieldInsnNode) ain; + if (owner != null && !fin.owner.equals(owner)) { + return false; + } + if (name != null && !fin.name.equals(name)) { + return false; + } + if (desc != null && !fin.desc.equals(desc)) { + return false; + } + return true; + } + + public int getOpcode() { + return opcode; + } + + public String getOwner() { + return owner; + } + + public String getName() { + return name; + } + + public String getDesc() { + return desc; + } + + @Override + public String toString() { + return "FieldInsnStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + ", opcode=" + this.opcode + + ", owner='" + this.owner + '\'' + + ", name='" + this.name + '\'' + + ", desc='" + this.desc + '\'' + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/IterableInsnMatcher.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/IterableInsnMatcher.java new file mode 100644 index 00000000..d15d9426 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/IterableInsnMatcher.java @@ -0,0 +1,176 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; + +import com.google.common.base.Preconditions; +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.InsnList; + +public class IterableInsnMatcher { + + private final List> steps = new ArrayList<>(); + private final Map, Optional> replacements = new HashMap<>(); + private final List endInserts = new ArrayList<>(4); + private boolean hasMatched; + private boolean hasReplaced; + + public IterableInsnMatcher() { + } + + public IterableInsnMatcher(Consumer initializer) { + initializer.accept(this); + } + + public > T addStep(T step) { + Preconditions.checkState(!hasReplaced, "Reset before adding steps needed, already matched & replaced"); + Preconditions.checkState(!hasMatched, "Reset before adding steps needed, already matched"); + for (IterableStep s : steps) { + if (s == step) { + throw new IllegalArgumentException("step " + step + " is already added"); + } + } + steps.add(step); + return step; + } + + /** + * leaves the given iterator just after the first matching instruction + */ + public boolean match(ListIterator iterator) { + if (steps.isEmpty()) { + throw new IllegalStateException("At least 1 step is required"); + } + while (iterator.hasNext()) { + if (match0(iterator)) { + return true; + } + } + return false; + } + + /** + * leaves the given iterator just after the first matching instruction + */ + public boolean matchImmediately(ListIterator iterator) { + if (steps.isEmpty()) { + throw new IllegalStateException("At least 1 step is required"); + } + if (!iterator.hasNext()) { + return false; + } + return match0(iterator); + } + + private boolean match0(ListIterator iterator) { + int size = steps.size(); + int stepCount = 0; + AbstractInsnNode next = iterator.next(); + while (next != null) { + IterableStep step = steps.get(stepCount); + if (!step.match(next)) { + break; + } + stepCount++; + if (stepCount == size) { + hasMatched = true; + return true; + } + next = next.getNext(); + } + return false; + } + + public void addReplacement(IterableStep step, AbstractInsnNode replacement) { + Preconditions.checkArgument(steps.contains(step), "Step was not added - cannot add replacement for it. Step: " + step); + Preconditions.checkState(hasMatched, "Cannot add replacements before matching"); + replacements.put(step, Optional.ofNullable(replacement)); + } + + public void addRemoval(IterableStep step) { + addReplacement(step, null); + } + + public void addEndInsert(AbstractInsnNode step) { + endInserts.add(step); + } + + public void setRemoveAll() { + steps.forEach(this::addRemoval); + } + + public void setRemoval(int stepNumber) { + addRemoval(steps.get(stepNumber)); + } + + public void setReplacement(int stepNumber, AbstractInsnNode replacement) { + addReplacement(steps.get(stepNumber), replacement); + } + + /** + * perform previously set up replacement / removal + * + * iterator afterwards will be just after the last matched step + * + * @param iterator must be in same state as it was after match was called + */ + @SuppressWarnings("OptionalAssignedToNull") + public boolean replace(ListIterator iterator) { + Preconditions.checkState(iterator.hasPrevious(), "iterator not genuine, no previous"); + iterator.previous(); + boolean change = false; + for (IterableStep step : steps) { + AbstractInsnNode next = iterator.next(); + Preconditions.checkState(step.getCaptured() == next, + "iterator not genuine, expected " + Utils.prettyprint(step.getCaptured()) + " but got " + Utils.prettyprint(next)); + Optional replacement = replacements.get(step); + if (replacement != null) { + if (replacement.isPresent()) { + iterator.set(replacement.get()); + } else { + iterator.remove(); + } + change = true; + } + } + if (!endInserts.isEmpty()) { + endInserts.forEach(iterator::add); + return true; + } + return change; + } + + /** + * resets internals and replacements for next match + */ + public void reset() { + for (IterableStep step : steps) { + step.reset(); + } + hasMatched = false; + hasReplaced = false; + endInserts.clear(); + } + + public static int removeAll(IterableInsnMatcher matcher, InsnList insns) { + int count = 0; + ListIterator it = insns.iterator(); + while (it.hasNext()) { + if (matcher.match(it)) { + matcher.setRemoveAll(); + if (!matcher.replace(it)) { + throw new IllegalStateException("Could not find anything to replace!"); + } + matcher.reset(); + ++count; + } + } + return count; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/IterableStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/IterableStep.java new file mode 100644 index 00000000..366c5c82 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/IterableStep.java @@ -0,0 +1,49 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; + +public abstract class IterableStep implements Opcodes { + + private T captured; + + public final void reset() { + captured = null; + reset0(); + } + + public void reset0() { + + } + + public final T getCaptured() { + return captured; + } + + public final boolean match(AbstractInsnNode ain) { + if (tryMatch(ain)) { + captured = (T) ain; + return true; + } + return false; + } + + public abstract boolean tryMatch(AbstractInsnNode ain); + + @Override + public final int hashCode() { + return super.hashCode(); + } + + @Override + public final boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public String toString() { + return "IterableStep{" + + "captured=" + this.captured + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/MethodInsnStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/MethodInsnStep.java new file mode 100644 index 00000000..d80f6562 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/MethodInsnStep.java @@ -0,0 +1,81 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import java.util.function.Predicate; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.apache.commons.lang3.Validate; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; + +public class MethodInsnStep extends IterableStep { + + private final int opcode; + private final String owner; + private final String name; + private final String desc; + private final Predicate additional; + + public MethodInsnStep(int opcode, String owner, String name, String desc) { + this(opcode, owner, name, desc, null); + } + + public MethodInsnStep(int opcode, String owner, String name, String desc, Predicate additional) { + Validate.inclusiveBetween(INVOKEVIRTUAL, INVOKEINTERFACE, opcode, "Invalid MethodInsn opcode %s", opcode); + this.opcode = opcode; + this.owner = owner; + this.name = name; + this.desc = desc; + this.additional = additional; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + if (ain.getOpcode() != opcode) { + return false; + } + if (!(ain instanceof MethodInsnNode)) { + return false; + } + MethodInsnNode min = (MethodInsnNode) ain; + if (owner != null && !min.owner.equals(owner)) { + return false; + } + if (name != null && !min.name.equals(name)) { + return false; + } + if (desc != null && !min.desc.equals(desc)) { + return false; + } + if (additional != null && !additional.test(min)) { + return false; + } + return true; + } + + public int getOpcode() { + return opcode; + } + + public String getOwner() { + return owner; + } + + public String getName() { + return name; + } + + public String getDesc() { + return desc; + } + + @Override + public String toString() { + return "MethodInsnStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + ", opcode=" + this.opcode + + ", owner='" + this.owner + '\'' + + ", name='" + this.name + '\'' + + ", desc='" + this.desc + '\'' + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/NoSideEffectLoad1SlotStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/NoSideEffectLoad1SlotStep.java new file mode 100644 index 00000000..59a60d66 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/NoSideEffectLoad1SlotStep.java @@ -0,0 +1,51 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; + +public class NoSideEffectLoad1SlotStep extends IterableStep { + + private final boolean alsoMatchStackLoad; + + public NoSideEffectLoad1SlotStep(boolean alsoMatchStackLoad) { + this.alsoMatchStackLoad = alsoMatchStackLoad; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + switch (ain.getOpcode()) { + case ACONST_NULL: + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + case FCONST_0: + case FCONST_1: + case FCONST_2: + case BIPUSH: + case SIPUSH: + return true; + case ILOAD: + case FLOAD: + case ALOAD: + return alsoMatchStackLoad; + case LDC: + Object obj = ((LdcInsnNode) ain).cst; + return !(obj instanceof Double || obj instanceof Long || obj instanceof Type); + default: + return false; + } + } + + @Override + public String toString() { + return "NoSideEffectLoad1SlotStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/NoSideEffectLoad2SlotStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/NoSideEffectLoad2SlotStep.java new file mode 100644 index 00000000..fb2ea3c4 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/NoSideEffectLoad2SlotStep.java @@ -0,0 +1,40 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; + +public class NoSideEffectLoad2SlotStep extends IterableStep { + + private final boolean alsoMatchStackLoad; + + public NoSideEffectLoad2SlotStep(boolean alsoMatchStackLoad) { + this.alsoMatchStackLoad = alsoMatchStackLoad; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + switch (ain.getOpcode()) { + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + return true; + case LLOAD: + case DLOAD: + return alsoMatchStackLoad; + case LDC: + Object obj = ((LdcInsnNode) ain).cst; + return obj instanceof Double || obj instanceof Long; + default: + return false; + } + } + + @Override + public String toString() { + return "NoSideEffectLoad2SlotStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/SimpleStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/SimpleStep.java new file mode 100644 index 00000000..be9af0da --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/SimpleStep.java @@ -0,0 +1,62 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import java.util.Arrays; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.apache.commons.lang3.Validate; +import org.objectweb.asm.tree.AbstractInsnNode; + +public class SimpleStep extends IterableStep { + + private final int[] opcodes; + + public SimpleStep(int... opcodes) { + this.opcodes = opcodes; + } + + public static SimpleStep ofOpcodeRangeInclusive(int start, int end) { + Validate.isTrue(start < end, "start opcode %s not smaller than end opcode %s", start, end); + int[] arr = new int[end - start + 1]; + int j = 0; + for (int i = start; i <= end; i++) { + arr[j++] = i; + } + return new SimpleStep(arr); + } + + public static SimpleStep ofOpcodeRangeInclusive(int start, int end, int[] additional) { + Validate.isTrue(start < end, "start opcode %s not smaller than end opcode %s", start, end); + int[] arr = new int[end - start + 1 + additional.length]; + int j = 0; + for (int i = start; i <= end; i++) { + arr[j++] = i; + } + for (int i : additional) { + arr[j++] = i; + } + return new SimpleStep(arr); + } + + public int[] getOpcodes() { + return opcodes; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + int ainOpcode = ain.getOpcode(); + for (int opcode : opcodes) { + if (ainOpcode == opcode) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "SimpleStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + ", opcodes=" + Arrays.toString(this.opcodes) + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/StackStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/StackStep.java new file mode 100644 index 00000000..99c86efc --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/StackStep.java @@ -0,0 +1,63 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.VarInsnNode; + +public class StackStep extends IterableStep { + + private final Boolean isLoad; + private final boolean twoSlotSize; + + /** + * + * @param isLoad true: load, false: store, null: both + */ + public StackStep(Boolean isLoad, boolean twoSlotSize) { + this.isLoad = isLoad; + this.twoSlotSize = twoSlotSize; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + boolean isStackInsn = ain instanceof VarInsnNode; + if (!isStackInsn) { + return false; + } + int opcode = ain.getOpcode(); + if (isLoad == null) { + switch (opcode) { + case DLOAD: + case LLOAD: + case DSTORE: + case LSTORE: + return twoSlotSize; + default: + return !twoSlotSize; + } + } + if (isLoad) { + if (opcode == DLOAD || opcode == LLOAD) { + return twoSlotSize; + } else if (opcode <= ALOAD) { + return !twoSlotSize; + } + } else { + if (opcode == DSTORE || opcode == LSTORE) { + return twoSlotSize; + } else if (opcode > ALOAD) { + return !twoSlotSize; + } + } + return false; + } + + @Override + public String toString() { + return "StackStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + ", isLoad=" + this.isLoad + + ", twoSlotSize=" + this.twoSlotSize + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/TypeInsnStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/TypeInsnStep.java new file mode 100644 index 00000000..40130f44 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/iterablematcher/TypeInsnStep.java @@ -0,0 +1,39 @@ +package com.javadeobfuscator.deobfuscator.iterablematcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.apache.commons.lang3.Validate; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.TypeInsnNode; + +public class TypeInsnStep extends IterableStep { + + private final int opcode; + private final String desc; + + public TypeInsnStep(int opcode, String desc) { + Validate.isTrue(opcode == NEW || opcode == ANEWARRAY || opcode == CHECKCAST || opcode == INSTANCEOF, "Invalid TypeInsn opcode %s", opcode); + this.opcode = opcode; + this.desc = desc; + } + + @Override + public boolean tryMatch(AbstractInsnNode ain) { + if (ain.getOpcode() != opcode) { + return false; + } + if (!(ain instanceof TypeInsnNode)) { + return false; + } + TypeInsnNode tin = (TypeInsnNode) ain; + return desc == null || tin.desc.equals(desc); + } + + @Override + public String toString() { + return "TypeInsnStep{" + + "captured=" + Utils.prettyprint(this.getCaptured()) + + ", opcode=" + this.opcode + + ", desc='" + this.desc + '\'' + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/ANewArrayStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/ANewArrayStep.java new file mode 100644 index 00000000..45c5f0b9 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/ANewArrayStep.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import com.javadeobfuscator.deobfuscator.utils.TransformerHelper; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.TypeInsnNode; + +public class ANewArrayStep implements Step { + private final String type; + private final boolean basic; + + public ANewArrayStep(String type, boolean basic) { + this.type = type; + this.basic = basic; + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + if (now.getOpcode() != Opcodes.ANEWARRAY) { + return null; + } + + TypeInsnNode typeInsnNode = (TypeInsnNode) now; + if (!(basic ? TransformerHelper.basicType(typeInsnNode.desc) : typeInsnNode.desc).equals(type)) { + return null; + } + + return now.getNext(); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/CapturingStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/CapturingStep.java new file mode 100644 index 00000000..52d35546 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/CapturingStep.java @@ -0,0 +1,49 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; + +import java.util.ArrayList; +import java.util.List; + +public class CapturingStep implements Step { + private final Step capture; + private final String id; + + public CapturingStep(Step step, String id) { + this.capture = step; + this.id = id; + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + AbstractInsnNode start = now; + final AbstractInsnNode end = capture.tryMatch(matcher, now); + if (end == null) { + return null; + } + List captured = new ArrayList<>(); + for (; start != end; start = start.getNext()) { + if (Utils.isInstruction(start)) + captured.add(start); + } + matcher.capture(id, captured); + return end; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/FieldStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/FieldStep.java new file mode 100644 index 00000000..91d6c38c --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/FieldStep.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import com.javadeobfuscator.deobfuscator.utils.TransformerHelper; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; + +public class FieldStep implements Step { + private final int opcode; + private final String owner; + private final String name; + private final String desc; + private final boolean basic; + + public FieldStep(int opcode, String owner, String name, String desc, boolean basic) { + this.opcode = opcode; + this.owner = owner; + this.name = name; + this.desc = desc; + this.basic = basic; + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + if (opcode != -1 && now.getOpcode() != opcode) { + return null; + } + FieldInsnNode fieldInsnNode = (FieldInsnNode) now; + boolean ownerMatches = owner == null || fieldInsnNode.owner.equals(owner); + boolean nameMatches = name == null || fieldInsnNode.name.equals(name); + boolean descMatches = desc == null || (basic ? TransformerHelper.basicType(fieldInsnNode.desc) : fieldInsnNode.desc).equals(desc); + if (!ownerMatches || !nameMatches || !descMatches) { + return null; + } + return now.getNext(); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InstructionMatcher.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InstructionMatcher.java new file mode 100644 index 00000000..0e66a4e0 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InstructionMatcher.java @@ -0,0 +1,87 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InstructionMatcher { + public InstructionPattern getPattern() { + return pattern; + } + + private final InstructionPattern pattern; + private final AbstractInsnNode start; + private AbstractInsnNode end; + + public InstructionMatcher(InstructionPattern pattern, AbstractInsnNode start) { + this.pattern = pattern; + this.start = start; + } + + public boolean find() { + Step mainStep = new CapturingStep(new MultiStep(pattern.getSteps()), "all"); + end = mainStep.tryMatch(this, start); + if (end != null) { + end = Utils.getPrevious(end); // We want this inclusive + if (end == null) { + throw new RuntimeException("what?"); + } + return true; + } + return false; + } + + public AbstractInsnNode getStart() { + return start; + } + + public AbstractInsnNode getEnd() { + return end; + } + + private Map>> capturedInsns = new HashMap<>(); + + public void capture(String id, List captured) { + capturedInsns.computeIfAbsent(id, k -> new ArrayList<>()).add(captured); + } + + public List> getAllCapturedInstructions(String id) { + return capturedInsns.get(id); + } + + public List getCapturedInstructions(String id) { + List> captured = capturedInsns.get(id); + if (captured == null || captured.size() > 1) { + return null; + } + return captured.get(0); + } + + public AbstractInsnNode getCapturedInstruction(String id) { + List captured = getCapturedInstructions(id); + if (captured == null || captured.size() > 1) { + return null; + } + return captured.get(0); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InstructionPattern.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InstructionPattern.java new file mode 100644 index 00000000..994eb40d --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InstructionPattern.java @@ -0,0 +1,37 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.tree.AbstractInsnNode; + +// Really crappy instruction matcher +public class InstructionPattern { + + private final Step[] steps; + + public InstructionPattern(Step... steps) { + this.steps = steps; + } + + public InstructionMatcher matcher(AbstractInsnNode start) { + return new InstructionMatcher(this, start); + } + + public Step[] getSteps() { + return steps; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InvocationStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InvocationStep.java new file mode 100644 index 00000000..ffb44a0d --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/InvocationStep.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import com.javadeobfuscator.deobfuscator.utils.TransformerHelper; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; + +public class InvocationStep implements Step { + private final int opcode; + private final String owner; + private final String name; + private final String desc; + private final boolean basic; + + public InvocationStep(int opcode, String owner, String name, String desc, boolean basic) { + this.opcode = opcode; + this.owner = owner; + this.name = name; + this.desc = desc; + this.basic = basic; + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + if (opcode != -1 && now.getOpcode() != opcode) { + return null; + } + if (!(now instanceof MethodInsnNode)) { + return null; + } + MethodInsnNode methodInsnNode = (MethodInsnNode) now; + boolean ownerMatches = owner == null || methodInsnNode.owner.equals(owner); + boolean nameMatches = name == null || methodInsnNode.name.equals(name); + boolean descMatches = desc == null || (basic ? TransformerHelper.basicType(methodInsnNode.desc) : methodInsnNode.desc).equals(desc); + if (!ownerMatches || !nameMatches || !descMatches) { + return null; + } + return now.getNext(); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/LoadIntStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/LoadIntStep.java new file mode 100644 index 00000000..0bb7f4b7 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/LoadIntStep.java @@ -0,0 +1,41 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.LdcInsnNode; + +public class LoadIntStep extends OpcodeStep { + public LoadIntStep() { + super((a) -> + { + if(!(a instanceof LdcInsnNode)) + return true; + return ((LdcInsnNode)a).cst instanceof Integer; + },Opcodes.ICONST_M1, + Opcodes.ICONST_0, + Opcodes.ICONST_1, + Opcodes.ICONST_2, + Opcodes.ICONST_3, + Opcodes.ICONST_4, + Opcodes.ICONST_5, + Opcodes.LDC, + Opcodes.BIPUSH, + Opcodes.SIPUSH + ); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/MultiStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/MultiStep.java new file mode 100644 index 00000000..33a9d57d --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/MultiStep.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import com.javadeobfuscator.deobfuscator.utils.Utils; +import org.objectweb.asm.tree.AbstractInsnNode; + +public class MultiStep implements Step { + private final Step[] steps; + + public MultiStep(Step... steps) { + this.steps = steps; + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + for (Step step : steps) { + while (!Utils.isInstruction(now)) now = now.getNext(); + if (now == null) { + return null; + } + + AbstractInsnNode next = step.tryMatch(matcher, now); + if (next == null) { + return null; + } + now = next; + } + return now; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/NewArrayStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/NewArrayStep.java new file mode 100644 index 00000000..25e8a4c8 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/NewArrayStep.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.IntInsnNode; + +import java.util.ArrayList; +import java.util.List; + +public class NewArrayStep implements Step { + private final List sorts; + + public NewArrayStep(int... sorts) { + this.sorts = new ArrayList<>(); + for (int sort : sorts) { + this.sorts.add(sort); + } + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + if (now.getOpcode() == Opcodes.NEWARRAY && now instanceof IntInsnNode) { + if (this.sorts.contains(((IntInsnNode) now).operand)) { + return now.getNext(); + } + } + return null; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OpcodeStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OpcodeStep.java new file mode 100644 index 00000000..4023ffcd --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OpcodeStep.java @@ -0,0 +1,58 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.tree.AbstractInsnNode; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +public class OpcodeStep implements Step { + private final List wantOpcodes; + private Function function; + + public OpcodeStep(Function function, int... opcodes) { + this.function = function; + this.wantOpcodes = new ArrayList<>(); + for (int opcode : opcodes) { + this.wantOpcodes.add(opcode); + } + } + + public OpcodeStep(int... opcodes) { + this.wantOpcodes = new ArrayList<>(); + for (int opcode : opcodes) { + this.wantOpcodes.add(opcode); + } + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + if (this.wantOpcodes.contains(now.getOpcode()) && (function == null || function.apply(now))) { + return now.getNext(); + } + return null; + } + + @Override + public String toString() { + return "OpcodeStep{" + + "wantOpcodes=" + wantOpcodes + + '}'; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OptionalStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OptionalStep.java new file mode 100644 index 00000000..77f10d7f --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OptionalStep.java @@ -0,0 +1,41 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.tree.AbstractInsnNode; + +public class OptionalStep implements Step { + private final Step step; + + public OptionalStep(Step step) { + this.step = step; + } + + public OptionalStep(Step... steps) { + this.step = new MultiStep(steps); + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + AbstractInsnNode next = step.tryMatch(matcher, now); + if (next != null) { + return next; + } + // no progress was made + return now; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OrStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OrStep.java new file mode 100644 index 00000000..2b0e5c67 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/OrStep.java @@ -0,0 +1,38 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.tree.AbstractInsnNode; + +public class OrStep implements Step { + private final Step[] steps; + + public OrStep(Step... steps) { + this.steps = steps; + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + for (Step step : steps) { + AbstractInsnNode next = step.tryMatch(matcher, now); + if (next != null) { + return next; + } + } + return null; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/RepeatingStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/RepeatingStep.java new file mode 100644 index 00000000..debc5a73 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/RepeatingStep.java @@ -0,0 +1,62 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.tree.AbstractInsnNode; + +public class RepeatingStep implements Step { + + private final Step step; + private final int min; + private final int max; + + public RepeatingStep(Step step, int min, int max) { + this.step = step; + this.min = min; + this.max = max; + } + + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + if (max == -1) { + int amount = 0; + AbstractInsnNode next; + while (true) { + next = step.tryMatch(matcher, now); + if (next == null) { + break; + } else { + now = next; + } + amount++; + } + return (min == -1 || amount >= min) ? now : null; + } else { + AbstractInsnNode next; + for (int i = 0; i < max; i++) { + next = step.tryMatch(matcher, now); + if (next == null) { + if (min != -1 && i >= min) return now; + return null; + } else { + now = next; + } + } + return now; + } + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/Step.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/Step.java new file mode 100644 index 00000000..0a800f24 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/Step.java @@ -0,0 +1,23 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.tree.AbstractInsnNode; + +public interface Step { + AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now); +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/matcher/WildcardStep.java b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/WildcardStep.java new file mode 100644 index 00000000..8005edb5 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/matcher/WildcardStep.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.matcher; + +import org.objectweb.asm.tree.AbstractInsnNode; + +public class WildcardStep implements Step { + @Override + public AbstractInsnNode tryMatch(InstructionMatcher matcher, AbstractInsnNode now) { + return now.getNext(); + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/AnnotationVisitor.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/AnnotationVisitor.java deleted file mode 100644 index 41bb1e1e..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/AnnotationVisitor.java +++ /dev/null @@ -1,169 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A visitor to visit a Java annotation. The methods of this class must be - * called in the following order: ( visit | visitEnum | - * visitAnnotation | visitArray )* visitEnd. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public abstract class AnnotationVisitor { - - /** - * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - */ - protected final int api; - - /** - * The annotation visitor to which this visitor must delegate method calls. - * May be null. - */ - protected AnnotationVisitor av; - - /** - * Constructs a new {@link AnnotationVisitor}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - */ - public AnnotationVisitor(final int api) { - this(api, null); - } - - /** - * Constructs a new {@link AnnotationVisitor}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - * @param av - * the annotation visitor to which this visitor must delegate - * method calls. May be null. - */ - public AnnotationVisitor(final int api, final AnnotationVisitor av) { - if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { - throw new IllegalArgumentException(); - } - this.api = api; - this.av = av; - } - - /** - * Visits a primitive value of the annotation. - * - * @param name - * the value name. - * @param value - * the actual value, whose type must be {@link Byte}, - * {@link Boolean}, {@link Character}, {@link Short}, - * {@link Integer} , {@link Long}, {@link Float}, {@link Double}, - * {@link String} or {@link Type} or OBJECT or ARRAY sort. This - * value can also be an array of byte, boolean, short, char, int, - * long, float or double values (this is equivalent to using - * {@link #visitArray visitArray} and visiting each array element - * in turn, but is more convenient). - */ - public void visit(String name, Object value) { - if (av != null) { - av.visit(name, value); - } - } - - /** - * Visits an enumeration value of the annotation. - * - * @param name - * the value name. - * @param desc - * the class descriptor of the enumeration class. - * @param value - * the actual enumeration value. - */ - public void visitEnum(String name, String desc, String value) { - if (av != null) { - av.visitEnum(name, desc, value); - } - } - - /** - * Visits a nested annotation value of the annotation. - * - * @param name - * the value name. - * @param desc - * the class descriptor of the nested annotation class. - * @return a visitor to visit the actual nested annotation value, or - * null if this visitor is not interested in visiting this - * nested annotation. The nested annotation value must be fully - * visited before calling other methods on this annotation - * visitor. - */ - public AnnotationVisitor visitAnnotation(String name, String desc) { - if (av != null) { - return av.visitAnnotation(name, desc); - } - return null; - } - - /** - * Visits an array value of the annotation. Note that arrays of primitive - * types (such as byte, boolean, short, char, int, long, float or double) - * can be passed as value to {@link #visit visit}. This is what - * {@link ClassReader} does. - * - * @param name - * the value name. - * @return a visitor to visit the actual array value elements, or - * null if this visitor is not interested in visiting these - * values. The 'name' parameters passed to the methods of this - * visitor are ignored. All the array values must be visited - * before calling other methods on this annotation visitor. - */ - public AnnotationVisitor visitArray(String name) { - if (av != null) { - return av.visitArray(name); - } - return null; - } - - /** - * Visits the end of the annotation. - */ - public void visitEnd() { - if (av != null) { - av.visitEnd(); - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/AnnotationWriter.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/AnnotationWriter.java deleted file mode 100644 index 21441a1c..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/AnnotationWriter.java +++ /dev/null @@ -1,371 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * An {@link AnnotationVisitor} that generates annotations in bytecode form. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -final class AnnotationWriter extends AnnotationVisitor { - - /** - * The class writer to which this annotation must be added. - */ - private final ClassWriter cw; - - /** - * The number of values in this annotation. - */ - private int size; - - /** - * true if values are named, false otherwise. Annotation - * writers used for annotation default and annotation arrays use unnamed - * values. - */ - private final boolean named; - - /** - * The annotation values in bytecode form. This byte vector only contains - * the values themselves, i.e. the number of values must be stored as a - * unsigned short just before these bytes. - */ - private final ByteVector bv; - - /** - * The byte vector to be used to store the number of values of this - * annotation. See {@link #bv}. - */ - private final ByteVector parent; - - /** - * Where the number of values of this annotation must be stored in - * {@link #parent}. - */ - private final int offset; - - /** - * Next annotation writer. This field is used to store annotation lists. - */ - AnnotationWriter next; - - /** - * Previous annotation writer. This field is used to store annotation lists. - */ - AnnotationWriter prev; - - // ------------------------------------------------------------------------ - // Constructor - // ------------------------------------------------------------------------ - - /** - * Constructs a new {@link AnnotationWriter}. - * - * @param cw - * the class writer to which this annotation must be added. - * @param named - * true if values are named, false otherwise. - * @param bv - * where the annotation values must be stored. - * @param parent - * where the number of annotation values must be stored. - * @param offset - * where in parent the number of annotation values must - * be stored. - */ - AnnotationWriter(final ClassWriter cw, final boolean named, - final ByteVector bv, final ByteVector parent, final int offset) { - super(Opcodes.ASM5); - this.cw = cw; - this.named = named; - this.bv = bv; - this.parent = parent; - this.offset = offset; - } - - // ------------------------------------------------------------------------ - // Implementation of the AnnotationVisitor abstract class - // ------------------------------------------------------------------------ - - @Override - public void visit(final String name, final Object value) { - ++size; - if (named) { - bv.putShort(cw.newUTF8(name)); - } - if (value instanceof String) { - bv.put12('s', cw.newUTF8((String) value)); - } else if (value instanceof Byte) { - bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); - } else if (value instanceof Boolean) { - int v = ((Boolean) value).booleanValue() ? 1 : 0; - bv.put12('Z', cw.newInteger(v).index); - } else if (value instanceof Character) { - bv.put12('C', cw.newInteger(((Character) value).charValue()).index); - } else if (value instanceof Short) { - bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); - } else if (value instanceof Type) { - bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); - } else if (value instanceof byte[]) { - byte[] v = (byte[]) value; - bv.put12('[', v.length); - for (int i = 0; i < v.length; i++) { - bv.put12('B', cw.newInteger(v[i]).index); - } - } else if (value instanceof boolean[]) { - boolean[] v = (boolean[]) value; - bv.put12('[', v.length); - for (int i = 0; i < v.length; i++) { - bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); - } - } else if (value instanceof short[]) { - short[] v = (short[]) value; - bv.put12('[', v.length); - for (int i = 0; i < v.length; i++) { - bv.put12('S', cw.newInteger(v[i]).index); - } - } else if (value instanceof char[]) { - char[] v = (char[]) value; - bv.put12('[', v.length); - for (int i = 0; i < v.length; i++) { - bv.put12('C', cw.newInteger(v[i]).index); - } - } else if (value instanceof int[]) { - int[] v = (int[]) value; - bv.put12('[', v.length); - for (int i = 0; i < v.length; i++) { - bv.put12('I', cw.newInteger(v[i]).index); - } - } else if (value instanceof long[]) { - long[] v = (long[]) value; - bv.put12('[', v.length); - for (int i = 0; i < v.length; i++) { - bv.put12('J', cw.newLong(v[i]).index); - } - } else if (value instanceof float[]) { - float[] v = (float[]) value; - bv.put12('[', v.length); - for (int i = 0; i < v.length; i++) { - bv.put12('F', cw.newFloat(v[i]).index); - } - } else if (value instanceof double[]) { - double[] v = (double[]) value; - bv.put12('[', v.length); - for (int i = 0; i < v.length; i++) { - bv.put12('D', cw.newDouble(v[i]).index); - } - } else { - Item i = cw.newConstItem(value); - bv.put12(".s.IFJDCS".charAt(i.type), i.index); - } - } - - @Override - public void visitEnum(final String name, final String desc, - final String value) { - ++size; - if (named) { - bv.putShort(cw.newUTF8(name)); - } - bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); - } - - @Override - public AnnotationVisitor visitAnnotation(final String name, - final String desc) { - ++size; - if (named) { - bv.putShort(cw.newUTF8(name)); - } - // write tag and type, and reserve space for values count - bv.put12('@', cw.newUTF8(desc)).putShort(0); - return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); - } - - @Override - public AnnotationVisitor visitArray(final String name) { - ++size; - if (named) { - bv.putShort(cw.newUTF8(name)); - } - // write tag, and reserve space for array size - bv.put12('[', 0); - return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); - } - - @Override - public void visitEnd() { - if (parent != null) { - byte[] data = parent.data; - data[offset] = (byte) (size >>> 8); - data[offset + 1] = (byte) size; - } - } - - // ------------------------------------------------------------------------ - // Utility methods - // ------------------------------------------------------------------------ - - /** - * Returns the size of this annotation writer list. - * - * @return the size of this annotation writer list. - */ - int getSize() { - int size = 0; - AnnotationWriter aw = this; - while (aw != null) { - size += aw.bv.length; - aw = aw.next; - } - return size; - } - - /** - * Puts the annotations of this annotation writer list into the given byte - * vector. - * - * @param out - * where the annotations must be put. - */ - void put(final ByteVector out) { - int n = 0; - int size = 2; - AnnotationWriter aw = this; - AnnotationWriter last = null; - while (aw != null) { - ++n; - size += aw.bv.length; - aw.visitEnd(); // in case user forgot to call visitEnd - aw.prev = last; - last = aw; - aw = aw.next; - } - out.putInt(size); - out.putShort(n); - aw = last; - while (aw != null) { - out.putByteArray(aw.bv.data, 0, aw.bv.length); - aw = aw.prev; - } - } - - /** - * Puts the given annotation lists into the given byte vector. - * - * @param panns - * an array of annotation writer lists. - * @param off - * index of the first annotation to be written. - * @param out - * where the annotations must be put. - */ - static void put(final AnnotationWriter[] panns, final int off, - final ByteVector out) { - int size = 1 + 2 * (panns.length - off); - for (int i = off; i < panns.length; ++i) { - size += panns[i] == null ? 0 : panns[i].getSize(); - } - out.putInt(size).putByte(panns.length - off); - for (int i = off; i < panns.length; ++i) { - AnnotationWriter aw = panns[i]; - AnnotationWriter last = null; - int n = 0; - while (aw != null) { - ++n; - aw.visitEnd(); // in case user forgot to call visitEnd - aw.prev = last; - last = aw; - aw = aw.next; - } - out.putShort(n); - aw = last; - while (aw != null) { - out.putByteArray(aw.bv.data, 0, aw.bv.length); - aw = aw.prev; - } - } - } - - /** - * Puts the given type reference and type path into the given bytevector. - * LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported. - * - * @param typeRef - * a reference to the annotated type. See {@link TypeReference}. - * @param typePath - * the path to the annotated type argument, wildcard bound, array - * element type, or static inner type within 'typeRef'. May be - * null if the annotation targets 'typeRef' as a whole. - * @param out - * where the type reference and type path must be put. - */ - static void putTarget(int typeRef, TypePath typePath, ByteVector out) { - switch (typeRef >>> 24) { - case 0x00: // CLASS_TYPE_PARAMETER - case 0x01: // METHOD_TYPE_PARAMETER - case 0x16: // METHOD_FORMAL_PARAMETER - out.putShort(typeRef >>> 16); - break; - case 0x13: // FIELD - case 0x14: // METHOD_RETURN - case 0x15: // METHOD_RECEIVER - out.putByte(typeRef >>> 24); - break; - case 0x47: // CAST - case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT - case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT - case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT - case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT - out.putInt(typeRef); - break; - // case 0x10: // CLASS_EXTENDS - // case 0x11: // CLASS_TYPE_PARAMETER_BOUND - // case 0x12: // METHOD_TYPE_PARAMETER_BOUND - // case 0x17: // THROWS - // case 0x42: // EXCEPTION_PARAMETER - // case 0x43: // INSTANCEOF - // case 0x44: // NEW - // case 0x45: // CONSTRUCTOR_REFERENCE - // case 0x46: // METHOD_REFERENCE - default: - out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8); - break; - } - if (typePath == null) { - out.putByte(0); - } else { - int length = typePath.b[typePath.offset] * 2 + 1; - out.putByteArray(typePath.b, typePath.offset, length); - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Attribute.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Attribute.java deleted file mode 100644 index 86c7319c..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Attribute.java +++ /dev/null @@ -1,255 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A non standard class, field, method or code attribute. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public class Attribute { - - /** - * The type of this attribute. - */ - public final String type; - - /** - * The raw value of this attribute, used only for unknown attributes. - */ - byte[] value; - - /** - * The next attribute in this attribute list. May be null. - */ - Attribute next; - - /** - * Constructs a new empty attribute. - * - * @param type - * the type of the attribute. - */ - protected Attribute(final String type) { - this.type = type; - } - - /** - * Returns true if this type of attribute is unknown. The default - * implementation of this method always returns true. - * - * @return true if this type of attribute is unknown. - */ - public boolean isUnknown() { - return true; - } - - /** - * Returns true if this type of attribute is a code attribute. - * - * @return true if this type of attribute is a code attribute. - */ - public boolean isCodeAttribute() { - return false; - } - - /** - * Returns the labels corresponding to this attribute. - * - * @return the labels corresponding to this attribute, or null if - * this attribute is not a code attribute that contains labels. - */ - protected Label[] getLabels() { - return null; - } - - /** - * Reads a {@link #type type} attribute. This method must return a - * new {@link Attribute} object, of type {@link #type type}, - * corresponding to the len bytes starting at the given offset, in - * the given class reader. - * - * @param cr - * the class that contains the attribute to be read. - * @param off - * index of the first byte of the attribute's content in - * {@link ClassReader#b cr.b}. The 6 attribute header bytes, - * containing the type and the length of the attribute, are not - * taken into account here. - * @param len - * the length of the attribute's content. - * @param buf - * buffer to be used to call {@link ClassReader#readUTF8 - * readUTF8}, {@link ClassReader#readClass(int,char[]) readClass} - * or {@link ClassReader#readConst readConst}. - * @param codeOff - * index of the first byte of code's attribute content in - * {@link ClassReader#b cr.b}, or -1 if the attribute to be read - * is not a code attribute. The 6 attribute header bytes, - * containing the type and the length of the attribute, are not - * taken into account here. - * @param labels - * the labels of the method's code, or null if the - * attribute to be read is not a code attribute. - * @return a new {@link Attribute} object corresponding to the given - * bytes. - */ - protected Attribute read(final ClassReader cr, final int off, - final int len, final char[] buf, final int codeOff, - final Label[] labels) { - Attribute attr = new Attribute(type); - attr.value = new byte[len]; - System.arraycopy(cr.b, off, attr.value, 0, len); - return attr; - } - - /** - * Returns the byte array form of this attribute. - * - * @param cw - * the class to which this attribute must be added. This - * parameter can be used to add to the constant pool of this - * class the items that corresponds to this attribute. - * @param code - * the bytecode of the method corresponding to this code - * attribute, or null if this attribute is not a code - * attributes. - * @param len - * the length of the bytecode of the method corresponding to this - * code attribute, or null if this attribute is not a - * code attribute. - * @param maxStack - * the maximum stack size of the method corresponding to this - * code attribute, or -1 if this attribute is not a code - * attribute. - * @param maxLocals - * the maximum number of local variables of the method - * corresponding to this code attribute, or -1 if this attribute - * is not a code attribute. - * @return the byte array form of this attribute. - */ - protected ByteVector write(final ClassWriter cw, final byte[] code, - final int len, final int maxStack, final int maxLocals) { - ByteVector v = new ByteVector(); - v.data = value; - v.length = value.length; - return v; - } - - /** - * Returns the length of the attribute list that begins with this attribute. - * - * @return the length of the attribute list that begins with this attribute. - */ - final int getCount() { - int count = 0; - Attribute attr = this; - while (attr != null) { - count += 1; - attr = attr.next; - } - return count; - } - - /** - * Returns the size of all the attributes in this attribute list. - * - * @param cw - * the class writer to be used to convert the attributes into - * byte arrays, with the {@link #write write} method. - * @param code - * the bytecode of the method corresponding to these code - * attributes, or null if these attributes are not code - * attributes. - * @param len - * the length of the bytecode of the method corresponding to - * these code attributes, or null if these attributes - * are not code attributes. - * @param maxStack - * the maximum stack size of the method corresponding to these - * code attributes, or -1 if these attributes are not code - * attributes. - * @param maxLocals - * the maximum number of local variables of the method - * corresponding to these code attributes, or -1 if these - * attributes are not code attributes. - * @return the size of all the attributes in this attribute list. This size - * includes the size of the attribute headers. - */ - final int getSize(final ClassWriter cw, final byte[] code, final int len, - final int maxStack, final int maxLocals) { - Attribute attr = this; - int size = 0; - while (attr != null) { - cw.newUTF8(attr.type); - size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; - attr = attr.next; - } - return size; - } - - /** - * Writes all the attributes of this attribute list in the given byte - * vector. - * - * @param cw - * the class writer to be used to convert the attributes into - * byte arrays, with the {@link #write write} method. - * @param code - * the bytecode of the method corresponding to these code - * attributes, or null if these attributes are not code - * attributes. - * @param len - * the length of the bytecode of the method corresponding to - * these code attributes, or null if these attributes - * are not code attributes. - * @param maxStack - * the maximum stack size of the method corresponding to these - * code attributes, or -1 if these attributes are not code - * attributes. - * @param maxLocals - * the maximum number of local variables of the method - * corresponding to these code attributes, or -1 if these - * attributes are not code attributes. - * @param out - * where the attributes must be written. - */ - final void put(final ClassWriter cw, final byte[] code, final int len, - final int maxStack, final int maxLocals, final ByteVector out) { - Attribute attr = this; - while (attr != null) { - ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); - out.putShort(cw.newUTF8(attr.type)).putInt(b.length); - out.putByteArray(b.data, 0, b.length); - attr = attr.next; - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ByteVector.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ByteVector.java deleted file mode 100644 index 70605910..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ByteVector.java +++ /dev/null @@ -1,339 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A dynamically extensible vector of bytes. This class is roughly equivalent to - * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient. - * - * @author Eric Bruneton - */ -public class ByteVector { - - /** - * The content of this vector. - */ - byte[] data; - - /** - * Actual number of bytes in this vector. - */ - int length; - - /** - * Constructs a new {@link ByteVector ByteVector} with a default initial - * size. - */ - public ByteVector() { - data = new byte[64]; - } - - /** - * Constructs a new {@link ByteVector ByteVector} with the given initial - * size. - * - * @param initialSize - * the initial size of the byte vector to be constructed. - */ - public ByteVector(final int initialSize) { - data = new byte[initialSize]; - } - - /** - * Puts a byte into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param b - * a byte. - * @return this byte vector. - */ - public ByteVector putByte(final int b) { - int length = this.length; - if (length + 1 > data.length) { - enlarge(1); - } - data[length++] = (byte) b; - this.length = length; - return this; - } - - /** - * Puts two bytes into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param b1 - * a byte. - * @param b2 - * another byte. - * @return this byte vector. - */ - ByteVector put11(final int b1, final int b2) { - int length = this.length; - if (length + 2 > data.length) { - enlarge(2); - } - byte[] data = this.data; - data[length++] = (byte) b1; - data[length++] = (byte) b2; - this.length = length; - return this; - } - - /** - * Puts a short into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param s - * a short. - * @return this byte vector. - */ - public ByteVector putShort(final int s) { - int length = this.length; - if (length + 2 > data.length) { - enlarge(2); - } - byte[] data = this.data; - data[length++] = (byte) (s >>> 8); - data[length++] = (byte) s; - this.length = length; - return this; - } - - /** - * Puts a byte and a short into this byte vector. The byte vector is - * automatically enlarged if necessary. - * - * @param b - * a byte. - * @param s - * a short. - * @return this byte vector. - */ - ByteVector put12(final int b, final int s) { - int length = this.length; - if (length + 3 > data.length) { - enlarge(3); - } - byte[] data = this.data; - data[length++] = (byte) b; - data[length++] = (byte) (s >>> 8); - data[length++] = (byte) s; - this.length = length; - return this; - } - - /** - * Puts an int into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param i - * an int. - * @return this byte vector. - */ - public ByteVector putInt(final int i) { - int length = this.length; - if (length + 4 > data.length) { - enlarge(4); - } - byte[] data = this.data; - data[length++] = (byte) (i >>> 24); - data[length++] = (byte) (i >>> 16); - data[length++] = (byte) (i >>> 8); - data[length++] = (byte) i; - this.length = length; - return this; - } - - /** - * Puts a long into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param l - * a long. - * @return this byte vector. - */ - public ByteVector putLong(final long l) { - int length = this.length; - if (length + 8 > data.length) { - enlarge(8); - } - byte[] data = this.data; - int i = (int) (l >>> 32); - data[length++] = (byte) (i >>> 24); - data[length++] = (byte) (i >>> 16); - data[length++] = (byte) (i >>> 8); - data[length++] = (byte) i; - i = (int) l; - data[length++] = (byte) (i >>> 24); - data[length++] = (byte) (i >>> 16); - data[length++] = (byte) (i >>> 8); - data[length++] = (byte) i; - this.length = length; - return this; - } - - /** - * Puts an UTF8 string into this byte vector. The byte vector is - * automatically enlarged if necessary. - * - * @param s - * a String whose UTF8 encoded length must be less than 65536. - * @return this byte vector. - */ - public ByteVector putUTF8(final String s) { - int charLength = s.length(); - if (charLength > 65535) { - throw new IllegalArgumentException(); - } - int len = length; - if (len + 2 + charLength > data.length) { - enlarge(2 + charLength); - } - byte[] data = this.data; - // optimistic algorithm: instead of computing the byte length and then - // serializing the string (which requires two loops), we assume the byte - // length is equal to char length (which is the most frequent case), and - // we start serializing the string right away. During the serialization, - // if we find that this assumption is wrong, we continue with the - // general method. - data[len++] = (byte) (charLength >>> 8); - data[len++] = (byte) charLength; - for (int i = 0; i < charLength; ++i) { - char c = s.charAt(i); - if (c >= '\001' && c <= '\177') { - data[len++] = (byte) c; - } else { - length = len; - return encodeUTF8(s, i, 65535); - } - } - length = len; - return this; - } - - /** - * Puts an UTF8 string into this byte vector. The byte vector is - * automatically enlarged if necessary. The string length is encoded in two - * bytes before the encoded characters, if there is space for that (i.e. if - * this.length - i - 2 >= 0). - * - * @param s - * the String to encode. - * @param i - * the index of the first character to encode. The previous - * characters are supposed to have already been encoded, using - * only one byte per character. - * @param maxByteLength - * the maximum byte length of the encoded string, including the - * already encoded characters. - * @return this byte vector. - */ - ByteVector encodeUTF8(final String s, int i, int maxByteLength) { - int charLength = s.length(); - int byteLength = i; - char c; - for (int j = i; j < charLength; ++j) { - c = s.charAt(j); - if (c >= '\001' && c <= '\177') { - byteLength++; - } else if (c > '\u07FF') { - byteLength += 3; - } else { - byteLength += 2; - } - } - if (byteLength > maxByteLength) { - throw new IllegalArgumentException(); - } - int start = length - i - 2; - if (start >= 0) { - data[start] = (byte) (byteLength >>> 8); - data[start + 1] = (byte) byteLength; - } - if (length + byteLength - i > data.length) { - enlarge(byteLength - i); - } - int len = length; - for (int j = i; j < charLength; ++j) { - c = s.charAt(j); - if (c >= '\001' && c <= '\177') { - data[len++] = (byte) c; - } else if (c > '\u07FF') { - data[len++] = (byte) (0xE0 | c >> 12 & 0xF); - data[len++] = (byte) (0x80 | c >> 6 & 0x3F); - data[len++] = (byte) (0x80 | c & 0x3F); - } else { - data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); - data[len++] = (byte) (0x80 | c & 0x3F); - } - } - length = len; - return this; - } - - /** - * Puts an array of bytes into this byte vector. The byte vector is - * automatically enlarged if necessary. - * - * @param b - * an array of bytes. May be null to put len - * null bytes into this byte vector. - * @param off - * index of the fist byte of b that must be copied. - * @param len - * number of bytes of b that must be copied. - * @return this byte vector. - */ - public ByteVector putByteArray(final byte[] b, final int off, final int len) { - if (length + len > data.length) { - enlarge(len); - } - if (b != null) { - System.arraycopy(b, off, data, length, len); - } - length += len; - return this; - } - - /** - * Enlarge this byte vector so that it can receive n more bytes. - * - * @param size - * number of additional bytes that this byte vector should be - * able to receive. - */ - private void enlarge(final int size) { - int length1 = 2 * data.length; - int length2 = length + size; - byte[] newData = new byte[length1 > length2 ? length1 : length2]; - System.arraycopy(data, 0, newData, 0, length); - data = newData; - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassReader.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassReader.java deleted file mode 100644 index 22e022b1..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassReader.java +++ /dev/null @@ -1,2513 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; - -/** - * A Java class parser to make a {@link ClassVisitor} visit an existing class. - * This class parses a byte array conforming to the Java class file format and - * calls the appropriate visit methods of a given class visitor for each field, - * method and bytecode instruction encountered. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public class ClassReader { - - /** - * True to enable signatures support. - */ - static final boolean SIGNATURES = true; - - /** - * True to enable annotations support. - */ - static final boolean ANNOTATIONS = true; - - /** - * True to enable stack map frames support. - */ - static final boolean FRAMES = true; - - /** - * True to enable bytecode writing support. - */ - static final boolean WRITER = true; - - /** - * True to enable JSR_W and GOTO_W support. - */ - static final boolean RESIZE = true; - - /** - * Flag to skip method code. If this class is set CODE - * attribute won't be visited. This can be used, for example, to retrieve - * annotations for methods and method parameters. - */ - public static final int SKIP_CODE = 1; - - /** - * Flag to skip the debug information in the class. If this flag is set the - * debug information of the class is not visited, i.e. the - * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and - * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be - * called. - */ - public static final int SKIP_DEBUG = 2; - - /** - * Flag to skip the stack map frames in the class. If this flag is set the - * stack map frames of the class is not visited, i.e. the - * {@link MethodVisitor#visitFrame visitFrame} method will not be called. - * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is - * used: it avoids visiting frames that will be ignored and recomputed from - * scratch in the class writer. - */ - public static final int SKIP_FRAMES = 4; - - /** - * Flag to expand the stack map frames. By default stack map frames are - * visited in their original format (i.e. "expanded" for classes whose - * version is less than V1_6, and "compressed" for the other classes). If - * this flag is set, stack map frames are always visited in expanded format - * (this option adds a decompression/recompression step in ClassReader and - * ClassWriter which degrades performances quite a lot). - */ - public static final int EXPAND_FRAMES = 8; - - /** - * The class to be parsed. The content of this array must not be - * modified. This field is intended for {@link Attribute} sub classes, and - * is normally not needed by class generators or adapters. - */ - public final byte[] b; - - /** - * The start index of each constant pool item in {@link #b b}, plus one. The - * one byte offset skips the constant pool item tag that indicates its type. - */ - private final int[] items; - - /** - * The String objects corresponding to the CONSTANT_Utf8 items. This cache - * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, - * which GREATLY improves performances (by a factor 2 to 3). This caching - * strategy could be extended to all constant pool items, but its benefit - * would not be so great for these items (because they are much less - * expensive to parse than CONSTANT_Utf8 items). - */ - private final String[] strings; - - /** - * Maximum length of the strings contained in the constant pool of the - * class. - */ - private final int maxStringLength; - - /** - * Start index of the class header information (access, name...) in - * {@link #b b}. - */ - public final int header; - - // ------------------------------------------------------------------------ - // Constructors - // ------------------------------------------------------------------------ - - /** - * Constructs a new {@link ClassReader} object. - * - * @param b - * the bytecode of the class to be read. - */ - public ClassReader(final byte[] b) { - this(b, 0, b.length); - } - - /** - * Constructs a new {@link ClassReader} object. - * - * @param b - * the bytecode of the class to be read. - * @param off - * the start offset of the class data. - * @param len - * the length of the class data. - */ - public ClassReader(final byte[] b, final int off, final int len) { - this.b = b; - - if (!(b[off] == (byte) 0xca && b[off + 1] == (byte) 0xfe && b[off + 2] == (byte) 0xba && b[off + 3] == (byte) 0xbe)) { - byte[] b1 = new byte[]{b[off], b[off + 1], b[off + 2], b[off + 3]}; - throw new IllegalArgumentException("Not a class file (" + Integer.toHexString(ByteBuffer.wrap(b1).getInt()) + ")"); - } - - // checks the class version - if (readShort(off + 6) > Opcodes.V1_8) { - throw new IllegalArgumentException(); - } - // parses the constant pool - items = new int[readUnsignedShort(off + 8)]; - int n = items.length; - strings = new String[n]; - int max = 0; - int index = off + 10; - for (int i = 1; i < n; ++i) { - items[i] = index + 1; - int size; - switch (b[index]) { - case ClassWriter.FIELD: - case ClassWriter.METH: - case ClassWriter.IMETH: - case ClassWriter.INT: - case ClassWriter.FLOAT: - case ClassWriter.NAME_TYPE: - case ClassWriter.INDY: - size = 5; - break; - case ClassWriter.LONG: - case ClassWriter.DOUBLE: - size = 9; - ++i; - break; - case ClassWriter.UTF8: - size = 3 + readUnsignedShort(index + 1); - if (size > max) { - max = size; - } - break; - case ClassWriter.HANDLE: - size = 4; - break; - // case ClassWriter.CLASS: - // case ClassWriter.STR: - // case ClassWriter.MTYPE - default: - size = 3; - break; - } - index += size; - } - maxStringLength = max; - // the class header information starts just after the constant pool - header = index; - } - - /** - * Returns the class's access flags (see {@link Opcodes}). This value may - * not reflect Deprecated and Synthetic flags when bytecode is before 1.5 - * and those flags are represented by attributes. - * - * @return the class access flags - * - * @see ClassVisitor#visit(int, int, String, String, String, String[]) - */ - public int getAccess() { - return readUnsignedShort(header); - } - - /** - * Returns the internal name of the class (see - * {@link Type#getInternalName() getInternalName}). - * - * @return the internal class name - * - * @see ClassVisitor#visit(int, int, String, String, String, String[]) - */ - public String getClassName() { - return readClass(header + 2, new char[maxStringLength]); - } - - /** - * Returns the internal of name of the super class (see - * {@link Type#getInternalName() getInternalName}). For interfaces, the - * super class is {@link Object}. - * - * @return the internal name of super class, or null for - * {@link Object} class. - * - * @see ClassVisitor#visit(int, int, String, String, String, String[]) - */ - public String getSuperName() { - return readClass(header + 4, new char[maxStringLength]); - } - - /** - * Returns the internal names of the class's interfaces (see - * {@link Type#getInternalName() getInternalName}). - * - * @return the array of internal names for all implemented interfaces or - * null. - * - * @see ClassVisitor#visit(int, int, String, String, String, String[]) - */ - public String[] getInterfaces() { - int index = header + 6; - int n = readUnsignedShort(index); - String[] interfaces = new String[n]; - if (n > 0) { - char[] buf = new char[maxStringLength]; - for (int i = 0; i < n; ++i) { - index += 2; - interfaces[i] = readClass(index, buf); - } - } - return interfaces; - } - - /** - * Copies the constant pool data into the given {@link ClassWriter}. Should - * be called before the {@link #accept(ClassVisitor,int)} method. - * - * @param classWriter - * the {@link ClassWriter} to copy constant pool into. - */ - void copyPool(final ClassWriter classWriter) { - char[] buf = new char[maxStringLength]; - int ll = items.length; - Item[] items2 = new Item[ll]; - for (int i = 1; i < ll; i++) { - int index = items[i]; - int tag = b[index - 1]; - Item item = new Item(i); - int nameType; - switch (tag) { - case ClassWriter.FIELD: - case ClassWriter.METH: - case ClassWriter.IMETH: - nameType = items[readUnsignedShort(index + 2)]; - item.set(tag, readClass(index, buf), readUTF8(nameType, buf), - readUTF8(nameType + 2, buf)); - break; - case ClassWriter.INT: - item.set(readInt(index)); - break; - case ClassWriter.FLOAT: - item.set(Float.intBitsToFloat(readInt(index))); - break; - case ClassWriter.NAME_TYPE: - item.set(tag, readUTF8(index, buf), readUTF8(index + 2, buf), - null); - break; - case ClassWriter.LONG: - item.set(readLong(index)); - ++i; - break; - case ClassWriter.DOUBLE: - item.set(Double.longBitsToDouble(readLong(index))); - ++i; - break; - case ClassWriter.UTF8: { - String s = strings[i]; - if (s == null) { - index = items[i]; - s = strings[i] = readUTF(index + 2, - readUnsignedShort(index), buf); - } - item.set(tag, s, null, null); - break; - } - case ClassWriter.HANDLE: { - int fieldOrMethodRef = items[readUnsignedShort(index + 1)]; - nameType = items[readUnsignedShort(fieldOrMethodRef + 2)]; - item.set(ClassWriter.HANDLE_BASE + readByte(index), - readClass(fieldOrMethodRef, buf), - readUTF8(nameType, buf), readUTF8(nameType + 2, buf)); - break; - } - case ClassWriter.INDY: - if (classWriter.bootstrapMethods == null) { - copyBootstrapMethods(classWriter, items2, buf); - } - nameType = items[readUnsignedShort(index + 2)]; - item.set(readUTF8(nameType, buf), readUTF8(nameType + 2, buf), - readUnsignedShort(index)); - break; - // case ClassWriter.STR: - // case ClassWriter.CLASS: - // case ClassWriter.MTYPE - default: - item.set(tag, readUTF8(index, buf), null, null); - break; - } - - int index2 = item.hashCode % items2.length; - item.next = items2[index2]; - items2[index2] = item; - } - - int off = items[1] - 1; - classWriter.pool.putByteArray(b, off, header - off); - classWriter.items = items2; - classWriter.threshold = (int) (0.75d * ll); - classWriter.index = ll; - } - - /** - * Copies the bootstrap method data into the given {@link ClassWriter}. - * Should be called before the {@link #accept(ClassVisitor,int)} method. - * - * @param classWriter - * the {@link ClassWriter} to copy bootstrap methods into. - */ - private void copyBootstrapMethods(final ClassWriter classWriter, - final Item[] items, final char[] c) { - // finds the "BootstrapMethods" attribute - int u = getAttributes(); - boolean found = false; - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - if ("BootstrapMethods".equals(attrName)) { - found = true; - break; - } - u += 6 + readInt(u + 4); - } - if (!found) { - return; - } - // copies the bootstrap methods in the class writer - int boostrapMethodCount = readUnsignedShort(u + 8); - for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) { - int position = v - u - 10; - int hashCode = readConst(readUnsignedShort(v), c).hashCode(); - for (int k = readUnsignedShort(v + 2); k > 0; --k) { - hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode(); - v += 2; - } - v += 4; - Item item = new Item(j); - item.set(position, hashCode & 0x7FFFFFFF); - int index = item.hashCode % items.length; - item.next = items[index]; - items[index] = item; - } - int attrSize = readInt(u + 4); - ByteVector bootstrapMethods = new ByteVector(attrSize + 62); - bootstrapMethods.putByteArray(b, u + 10, attrSize - 2); - classWriter.bootstrapMethodsCount = boostrapMethodCount; - classWriter.bootstrapMethods = bootstrapMethods; - } - - /** - * Constructs a new {@link ClassReader} object. - * - * @param is - * an input stream from which to read the class. - * @throws IOException - * if a problem occurs during reading. - */ - public ClassReader(final InputStream is) throws IOException { - this(readClass(is, false)); - } - - /** - * Constructs a new {@link ClassReader} object. - * - * @param name - * the binary qualified name of the class to be read. - * @throws IOException - * if an exception occurs during reading. - */ - public ClassReader(final String name) throws IOException { - this(readClass( - ClassLoader.getSystemResourceAsStream(name.replace('.', '/') - + ".class"), true)); - } - - /** - * Reads the bytecode of a class. - * - * @param is - * an input stream from which to read the class. - * @param close - * true to close the input stream after reading. - * @return the bytecode read from the given input stream. - * @throws IOException - * if a problem occurs during reading. - */ - private static byte[] readClass(final InputStream is, boolean close) - throws IOException { - if (is == null) { - throw new IOException("Class not found"); - } - try { - byte[] b = new byte[is.available()]; - int len = 0; - while (true) { - int n = is.read(b, len, b.length - len); - if (n == -1) { - if (len < b.length) { - byte[] c = new byte[len]; - System.arraycopy(b, 0, c, 0, len); - b = c; - } - return b; - } - len += n; - if (len == b.length) { - int last = is.read(); - if (last < 0) { - return b; - } - byte[] c = new byte[b.length + 1000]; - System.arraycopy(b, 0, c, 0, len); - c[len++] = (byte) last; - b = c; - } - } - } finally { - if (close) { - is.close(); - } - } - } - - // ------------------------------------------------------------------------ - // Public methods - // ------------------------------------------------------------------------ - - /** - * Makes the given visitor visit the Java class of this {@link ClassReader} - * . This class is the one specified in the constructor (see - * {@link #ClassReader(byte[]) ClassReader}). - * - * @param classVisitor - * the visitor that must visit this class. - * @param flags - * option flags that can be used to modify the default behavior - * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} - * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. - */ - public void accept(final ClassVisitor classVisitor, final int flags) { - accept(classVisitor, new Attribute[0], flags); - } - - /** - * Makes the given visitor visit the Java class of this {@link ClassReader}. - * This class is the one specified in the constructor (see - * {@link #ClassReader(byte[]) ClassReader}). - * - * @param classVisitor - * the visitor that must visit this class. - * @param attrs - * prototypes of the attributes that must be parsed during the - * visit of the class. Any attribute whose type is not equal to - * the type of one the prototypes will not be parsed: its byte - * array value will be passed unchanged to the ClassWriter. - * This may corrupt it if this value contains references to - * the constant pool, or has syntactic or semantic links with a - * class element that has been transformed by a class adapter - * between the reader and the writer. - * @param flags - * option flags that can be used to modify the default behavior - * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} - * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. - */ - public void accept(final ClassVisitor classVisitor, - final Attribute[] attrs, final int flags) { - int u = header; // current offset in the class file - char[] c = new char[maxStringLength]; // buffer used to read strings - - Context context = new Context(); - context.attrs = attrs; - context.flags = flags; - context.buffer = c; - - // reads the class declaration - int access = readUnsignedShort(u); - String name = readClass(u + 2, c); - String superClass = readClass(u + 4, c); - String[] interfaces = new String[readUnsignedShort(u + 6)]; - u += 8; - for (int i = 0; i < interfaces.length; ++i) { - interfaces[i] = readClass(u, c); - u += 2; - } - - // reads the class attributes - String signature = null; - String sourceFile = null; - String sourceDebug = null; - String enclosingOwner = null; - String enclosingName = null; - String enclosingDesc = null; - int anns = 0; - int ianns = 0; - int tanns = 0; - int itanns = 0; - int innerClasses = 0; - Attribute attributes = null; - - u = getAttributes(); - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if ("SourceFile".equals(attrName)) { - sourceFile = readUTF8(u + 8, c); - } else if ("InnerClasses".equals(attrName)) { - innerClasses = u + 8; - } else if ("EnclosingMethod".equals(attrName)) { - enclosingOwner = readClass(u + 8, c); - int item = readUnsignedShort(u + 10); - if (item != 0) { - enclosingName = readUTF8(items[item], c); - enclosingDesc = readUTF8(items[item] + 2, c); - } - } else if (SIGNATURES && "Signature".equals(attrName)) { - signature = readUTF8(u + 8, c); - } else if (ANNOTATIONS - && "RuntimeVisibleAnnotations".equals(attrName)) { - anns = u + 8; - } else if (ANNOTATIONS - && "RuntimeVisibleTypeAnnotations".equals(attrName)) { - tanns = u + 8; - } else if ("Deprecated".equals(attrName)) { - access |= Opcodes.ACC_DEPRECATED; - } else if ("Synthetic".equals(attrName)) { - access |= Opcodes.ACC_SYNTHETIC - | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; - } else if ("SourceDebugExtension".equals(attrName)) { - int len = readInt(u + 4); - sourceDebug = readUTF(u + 8, len, new char[len]); - } else if (ANNOTATIONS - && "RuntimeInvisibleAnnotations".equals(attrName)) { - ianns = u + 8; - } else if (ANNOTATIONS - && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { - itanns = u + 8; - } else if ("BootstrapMethods".equals(attrName)) { - int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; - for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { - bootstrapMethods[j] = v; - v += 2 + readUnsignedShort(v + 2) << 1; - } - context.bootstrapMethods = bootstrapMethods; - } else { - Attribute attr = readAttribute(attrs, attrName, u + 8, - readInt(u + 4), c, -1, null); - if (attr != null) { - attr.next = attributes; - attributes = attr; - } - } - u += 6 + readInt(u + 4); - } - - // visits the class declaration - classVisitor.visit(readInt(items[1] - 7), access, name, signature, - superClass, interfaces); - - // visits the source and debug info - if ((flags & SKIP_DEBUG) == 0 - && (sourceFile != null || sourceDebug != null)) { - classVisitor.visitSource(sourceFile, sourceDebug); - } - - // visits the outer class - if (enclosingOwner != null) { - classVisitor.visitOuterClass(enclosingOwner, enclosingName, - enclosingDesc); - } - - // visits the class annotations and type annotations - if (ANNOTATIONS && anns != 0) { - for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - classVisitor.visitAnnotation(readUTF8(v, c), true)); - } - } - if (ANNOTATIONS && ianns != 0) { - for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - classVisitor.visitAnnotation(readUTF8(v, c), false)); - } - } - if (ANNOTATIONS && tanns != 0) { - for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - classVisitor.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), true)); - } - } - if (ANNOTATIONS && itanns != 0) { - for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - classVisitor.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), false)); - } - } - - // visits the attributes - while (attributes != null) { - Attribute attr = attributes.next; - attributes.next = null; - classVisitor.visitAttribute(attributes); - attributes = attr; - } - - // visits the inner classes - if (innerClasses != 0) { - int v = innerClasses + 2; - for (int i = readUnsignedShort(innerClasses); i > 0; --i) { - classVisitor.visitInnerClass(readClass(v, c), - readClass(v + 2, c), readUTF8(v + 4, c), - readUnsignedShort(v + 6)); - v += 8; - } - } - - // visits the fields and methods - u = header + 10 + 2 * interfaces.length; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - u = readField(classVisitor, context, u); - } - u += 2; - for (int i = readUnsignedShort(u - 2); i > 0; --i) { - u = readMethod(classVisitor, context, u); - } - - // visits the end of the class - classVisitor.visitEnd(); - } - - /** - * Reads a field and makes the given visitor visit it. - * - * @param classVisitor - * the visitor that must visit the field. - * @param context - * information about the class being parsed. - * @param u - * the start offset of the field in the class file. - * @return the offset of the first byte following the field in the class. - */ - private int readField(final ClassVisitor classVisitor, - final Context context, int u) { - // reads the field declaration - char[] c = context.buffer; - int access = readUnsignedShort(u); - String name = readUTF8(u + 2, c); - String desc = readUTF8(u + 4, c); - u += 6; - - // reads the field attributes - String signature = null; - int anns = 0; - int ianns = 0; - int tanns = 0; - int itanns = 0; - Object value = null; - Attribute attributes = null; - - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if ("ConstantValue".equals(attrName)) { - int item = readUnsignedShort(u + 8); - value = item == 0 ? null : readConst(item, c); - } else if (SIGNATURES && "Signature".equals(attrName)) { - signature = readUTF8(u + 8, c); - } else if ("Deprecated".equals(attrName)) { - access |= Opcodes.ACC_DEPRECATED; - } else if ("Synthetic".equals(attrName)) { - access |= Opcodes.ACC_SYNTHETIC - | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; - } else if (ANNOTATIONS - && "RuntimeVisibleAnnotations".equals(attrName)) { - anns = u + 8; - } else if (ANNOTATIONS - && "RuntimeVisibleTypeAnnotations".equals(attrName)) { - tanns = u + 8; - } else if (ANNOTATIONS - && "RuntimeInvisibleAnnotations".equals(attrName)) { - ianns = u + 8; - } else if (ANNOTATIONS - && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { - itanns = u + 8; - } else { - Attribute attr = readAttribute(context.attrs, attrName, u + 8, - readInt(u + 4), c, -1, null); - if (attr != null) { - attr.next = attributes; - attributes = attr; - } - } - u += 6 + readInt(u + 4); - } - u += 2; - - // visits the field declaration - FieldVisitor fv = classVisitor.visitField(access, name, desc, - signature, value); - if (fv == null) { - return u; - } - - // visits the field annotations and type annotations - if (ANNOTATIONS && anns != 0) { - for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - fv.visitAnnotation(readUTF8(v, c), true)); - } - } - if (ANNOTATIONS && ianns != 0) { - for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - fv.visitAnnotation(readUTF8(v, c), false)); - } - } - if (ANNOTATIONS && tanns != 0) { - for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - fv.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), true)); - } - } - if (ANNOTATIONS && itanns != 0) { - for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - fv.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), false)); - } - } - - // visits the field attributes - while (attributes != null) { - Attribute attr = attributes.next; - attributes.next = null; - fv.visitAttribute(attributes); - attributes = attr; - } - - // visits the end of the field - fv.visitEnd(); - - return u; - } - - /** - * Reads a method and makes the given visitor visit it. - * - * @param classVisitor - * the visitor that must visit the method. - * @param context - * information about the class being parsed. - * @param u - * the start offset of the method in the class file. - * @return the offset of the first byte following the method in the class. - */ - private int readMethod(final ClassVisitor classVisitor, - final Context context, int u) { - // reads the method declaration - char[] c = context.buffer; - context.access = readUnsignedShort(u); - context.name = readUTF8(u + 2, c); - context.desc = readUTF8(u + 4, c); - u += 6; - - // reads the method attributes - int code = 0; - int exception = 0; - String[] exceptions = null; - String signature = null; - int methodParameters = 0; - int anns = 0; - int ianns = 0; - int tanns = 0; - int itanns = 0; - int dann = 0; - int mpanns = 0; - int impanns = 0; - int firstAttribute = u; - Attribute attributes = null; - - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if ("Code".equals(attrName)) { - if ((context.flags & SKIP_CODE) == 0) { - code = u + 8; - } - } else if ("Exceptions".equals(attrName)) { - exceptions = new String[readUnsignedShort(u + 8)]; - exception = u + 10; - for (int j = 0; j < exceptions.length; ++j) { - exceptions[j] = readClass(exception, c); - exception += 2; - } - } else if (SIGNATURES && "Signature".equals(attrName)) { - signature = readUTF8(u + 8, c); - } else if ("Deprecated".equals(attrName)) { - context.access |= Opcodes.ACC_DEPRECATED; - } else if (ANNOTATIONS - && "RuntimeVisibleAnnotations".equals(attrName)) { - anns = u + 8; - } else if (ANNOTATIONS - && "RuntimeVisibleTypeAnnotations".equals(attrName)) { - tanns = u + 8; - } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { - dann = u + 8; - } else if ("Synthetic".equals(attrName)) { - context.access |= Opcodes.ACC_SYNTHETIC - | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; - } else if (ANNOTATIONS - && "RuntimeInvisibleAnnotations".equals(attrName)) { - ianns = u + 8; - } else if (ANNOTATIONS - && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { - itanns = u + 8; - } else if (ANNOTATIONS - && "RuntimeVisibleParameterAnnotations".equals(attrName)) { - mpanns = u + 8; - } else if (ANNOTATIONS - && "RuntimeInvisibleParameterAnnotations".equals(attrName)) { - impanns = u + 8; - } else if ("MethodParameters".equals(attrName)) { - methodParameters = u + 8; - } else { - Attribute attr = readAttribute(context.attrs, attrName, u + 8, - readInt(u + 4), c, -1, null); - if (attr != null) { - attr.next = attributes; - attributes = attr; - } - } - u += 6 + readInt(u + 4); - } - u += 2; - - // visits the method declaration - MethodVisitor mv = classVisitor.visitMethod(context.access, - context.name, context.desc, signature, exceptions); - if (mv == null) { - return u; - } - - /* - * if the returned MethodVisitor is in fact a MethodWriter, it means - * there is no method adapter between the reader and the writer. If, in - * addition, the writer's constant pool was copied from this reader - * (mw.cw.cr == this), and the signature and exceptions of the method - * have not been changed, then it is possible to skip all visit events - * and just copy the original code of the method to the writer (the - * access, name and descriptor can have been changed, this is not - * important since they are not copied as is from the reader). - */ - if (WRITER && mv instanceof MethodWriter) { - MethodWriter mw = (MethodWriter) mv; - if (mw.cw.cr == this && signature == mw.signature) { - boolean sameExceptions = false; - if (exceptions == null) { - sameExceptions = mw.exceptionCount == 0; - } else if (exceptions.length == mw.exceptionCount) { - sameExceptions = true; - for (int j = exceptions.length - 1; j >= 0; --j) { - exception -= 2; - if (mw.exceptions[j] != readUnsignedShort(exception)) { - sameExceptions = false; - break; - } - } - } - if (sameExceptions) { - /* - * we do not copy directly the code into MethodWriter to - * save a byte array copy operation. The real copy will be - * done in ClassWriter.toByteArray(). - */ - mw.classReaderOffset = firstAttribute; - mw.classReaderLength = u - firstAttribute; - return u; - } - } - } - - // visit the method parameters - if (methodParameters != 0) { - for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) { - mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2)); - } - } - - // visits the method annotations - if (ANNOTATIONS && dann != 0) { - AnnotationVisitor dv = mv.visitAnnotationDefault(); - readAnnotationValue(dann, c, null, dv); - if (dv != null) { - dv.visitEnd(); - } - } - if (ANNOTATIONS && anns != 0) { - for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - mv.visitAnnotation(readUTF8(v, c), true)); - } - } - if (ANNOTATIONS && ianns != 0) { - for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { - v = readAnnotationValues(v + 2, c, true, - mv.visitAnnotation(readUTF8(v, c), false)); - } - } - if (ANNOTATIONS && tanns != 0) { - for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - mv.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), true)); - } - } - if (ANNOTATIONS && itanns != 0) { - for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { - v = readAnnotationTarget(context, v); - v = readAnnotationValues(v + 2, c, true, - mv.visitTypeAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), false)); - } - } - if (ANNOTATIONS && mpanns != 0) { - readParameterAnnotations(mv, context, mpanns, true); - } - if (ANNOTATIONS && impanns != 0) { - readParameterAnnotations(mv, context, impanns, false); - } - - // visits the method attributes - while (attributes != null) { - Attribute attr = attributes.next; - attributes.next = null; - mv.visitAttribute(attributes); - attributes = attr; - } - - // visits the method code - if (code != 0) { - mv.visitCode(); - readCode(mv, context, code); - } - - // visits the end of the method - mv.visitEnd(); - - return u; - } - - /** - * Reads the bytecode of a method and makes the given visitor visit it. - * - * @param mv - * the visitor that must visit the method's code. - * @param context - * information about the class being parsed. - * @param u - * the start offset of the code attribute in the class file. - */ - private void readCode(final MethodVisitor mv, final Context context, int u) { - // reads the header - byte[] b = this.b; - char[] c = context.buffer; - int maxStack = readUnsignedShort(u); - int maxLocals = readUnsignedShort(u + 2); - int codeLength = readInt(u + 4); - u += 8; - - // reads the bytecode to find the labels - int codeStart = u; - int codeEnd = u + codeLength; - Label[] labels = context.labels = new Label[codeLength + 2]; - readLabel(codeLength + 1, labels); - while (u < codeEnd) { - int offset = u - codeStart; - int opcode = b[u] & 0xFF; - switch (ClassWriter.TYPE[opcode]) { - case ClassWriter.NOARG_INSN: - case ClassWriter.IMPLVAR_INSN: - u += 1; - break; - case ClassWriter.LABEL_INSN: - readLabel(offset + readShort(u + 1), labels); - u += 3; - break; - case ClassWriter.LABELW_INSN: - readLabel(offset + readInt(u + 1), labels); - u += 5; - break; - case ClassWriter.WIDE_INSN: - opcode = b[u + 1] & 0xFF; - if (opcode == Opcodes.IINC) { - u += 6; - } else { - u += 4; - } - break; - case ClassWriter.TABL_INSN: - // skips 0 to 3 padding bytes - u = u + 4 - (offset & 3); - // reads instruction - readLabel(offset + readInt(u), labels); - for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) { - readLabel(offset + readInt(u + 12), labels); - u += 4; - } - u += 12; - break; - case ClassWriter.LOOK_INSN: - // skips 0 to 3 padding bytes - u = u + 4 - (offset & 3); - // reads instruction - readLabel(offset + readInt(u), labels); - for (int i = readInt(u + 4); i > 0; --i) { - readLabel(offset + readInt(u + 12), labels); - u += 8; - } - u += 8; - break; - case ClassWriter.VAR_INSN: - case ClassWriter.SBYTE_INSN: - case ClassWriter.LDC_INSN: - u += 2; - break; - case ClassWriter.SHORT_INSN: - case ClassWriter.LDCW_INSN: - case ClassWriter.FIELDORMETH_INSN: - case ClassWriter.TYPE_INSN: - case ClassWriter.IINC_INSN: - u += 3; - break; - case ClassWriter.ITFMETH_INSN: - case ClassWriter.INDYMETH_INSN: - u += 5; - break; - // case MANA_INSN: - default: - u += 4; - break; - } - } - - // reads the try catch entries to find the labels, and also visits them - for (int i = readUnsignedShort(u); i > 0; --i) { - Label start = readLabel(readUnsignedShort(u + 2), labels); - Label end = readLabel(readUnsignedShort(u + 4), labels); - Label handler = readLabel(readUnsignedShort(u + 6), labels); - String type = readUTF8(items[readUnsignedShort(u + 8)], c); - mv.visitTryCatchBlock(start, end, handler, type); - u += 8; - } - u += 2; - - // reads the code attributes - int[] tanns = null; // start index of each visible type annotation - int[] itanns = null; // start index of each invisible type annotation - int tann = 0; // current index in tanns array - int itann = 0; // current index in itanns array - int ntoff = -1; // next visible type annotation code offset - int nitoff = -1; // next invisible type annotation code offset - int varTable = 0; - int varTypeTable = 0; - boolean zip = true; - boolean unzip = (context.flags & EXPAND_FRAMES) != 0; - int stackMap = 0; - int stackMapSize = 0; - int frameCount = 0; - Context frame = null; - Attribute attributes = null; - - for (int i = readUnsignedShort(u); i > 0; --i) { - String attrName = readUTF8(u + 2, c); - if ("LocalVariableTable".equals(attrName)) { - if ((context.flags & SKIP_DEBUG) == 0) { - varTable = u + 8; - for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { - int label = readUnsignedShort(v + 10); - if (labels[label] == null) { - readLabel(label, labels).status |= Label.DEBUG; - } - label += readUnsignedShort(v + 12); - if (labels[label] == null) { - readLabel(label, labels).status |= Label.DEBUG; - } - v += 10; - } - } - } else if ("LocalVariableTypeTable".equals(attrName)) { - varTypeTable = u + 8; - } else if ("LineNumberTable".equals(attrName)) { - if ((context.flags & SKIP_DEBUG) == 0) { - for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { - int label = readUnsignedShort(v + 10); - if (labels[label] == null) { - readLabel(label, labels).status |= Label.DEBUG; - } - Label l = labels[label]; - while (l.line > 0) { - if (l.next == null) { - l.next = new Label(); - } - l = l.next; - } - l.line = readUnsignedShort(v + 12); - v += 4; - } - } - } else if (ANNOTATIONS - && "RuntimeVisibleTypeAnnotations".equals(attrName)) { - tanns = readTypeAnnotations(mv, context, u + 8, true); - ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1 - : readUnsignedShort(tanns[0] + 1); - } else if (ANNOTATIONS - && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { - itanns = readTypeAnnotations(mv, context, u + 8, false); - nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1 - : readUnsignedShort(itanns[0] + 1); - } else if (FRAMES && "StackMapTable".equals(attrName)) { - if ((context.flags & SKIP_FRAMES) == 0) { - stackMap = u + 10; - stackMapSize = readInt(u + 4); - frameCount = readUnsignedShort(u + 8); - } - /* - * here we do not extract the labels corresponding to the - * attribute content. This would require a full parsing of the - * attribute, which would need to be repeated in the second - * phase (see below). Instead the content of the attribute is - * read one frame at a time (i.e. after a frame has been - * visited, the next frame is read), and the labels it contains - * are also extracted one frame at a time. Thanks to the - * ordering of frames, having only a "one frame lookahead" is - * not a problem, i.e. it is not possible to see an offset - * smaller than the offset of the current insn and for which no - * Label exist. - */ - /* - * This is not true for UNINITIALIZED type offsets. We solve - * this by parsing the stack map table without a full decoding - * (see below). - */ - } else if (FRAMES && "StackMap".equals(attrName)) { - if ((context.flags & SKIP_FRAMES) == 0) { - zip = false; - stackMap = u + 10; - stackMapSize = readInt(u + 4); - frameCount = readUnsignedShort(u + 8); - } - /* - * IMPORTANT! here we assume that the frames are ordered, as in - * the StackMapTable attribute, although this is not guaranteed - * by the attribute format. - */ - } else { - for (int j = 0; j < context.attrs.length; ++j) { - if (context.attrs[j].type.equals(attrName)) { - Attribute attr = context.attrs[j].read(this, u + 8, - readInt(u + 4), c, codeStart - 8, labels); - if (attr != null) { - attr.next = attributes; - attributes = attr; - } - } - } - } - u += 6 + readInt(u + 4); - } - u += 2; - - // generates the first (implicit) stack map frame - if (FRAMES && stackMap != 0) { - /* - * for the first explicit frame the offset is not offset_delta + 1 - * but only offset_delta; setting the implicit frame offset to -1 - * allow the use of the "offset_delta + 1" rule in all cases - */ - frame = context; - frame.offset = -1; - frame.mode = 0; - frame.localCount = 0; - frame.localDiff = 0; - frame.stackCount = 0; - frame.local = new Object[maxLocals]; - frame.stack = new Object[maxStack]; - if (unzip) { - getImplicitFrame(context); - } - /* - * Finds labels for UNINITIALIZED frame types. Instead of decoding - * each element of the stack map table, we look for 3 consecutive - * bytes that "look like" an UNINITIALIZED type (tag 8, offset - * within code bounds, NEW instruction at this offset). We may find - * false positives (i.e. not real UNINITIALIZED types), but this - * should be rare, and the only consequence will be the creation of - * an unneeded label. This is better than creating a label for each - * NEW instruction, and faster than fully decoding the whole stack - * map table. - */ - for (int i = stackMap; i < stackMap + stackMapSize - 2; ++i) { - if (b[i] == 8) { // UNINITIALIZED FRAME TYPE - int v = readUnsignedShort(i + 1); - if (v >= 0 && v < codeLength) { - if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) { - readLabel(v, labels); - } - } - } - } - } - - // visits the instructions - u = codeStart; - while (u < codeEnd) { - int offset = u - codeStart; - - // visits the label and line number for this offset, if any - Label l = labels[offset]; - if (l != null) { - Label next = l.next; - l.next = null; - mv.visitLabel(l); - if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) { - mv.visitLineNumber(l.line, l); - while (next != null) { - mv.visitLineNumber(next.line, l); - next = next.next; - } - } - } - - // visits the frame for this offset, if any - while (FRAMES && frame != null - && (frame.offset == offset || frame.offset == -1)) { - // if there is a frame for this offset, makes the visitor visit - // it, and reads the next frame if there is one. - if (frame.offset != -1) { - if (!zip || unzip) { - mv.visitFrame(Opcodes.F_NEW, frame.localCount, - frame.local, frame.stackCount, frame.stack); - } else { - mv.visitFrame(frame.mode, frame.localDiff, frame.local, - frame.stackCount, frame.stack); - } - } - if (frameCount > 0) { - stackMap = readFrame(stackMap, zip, unzip, frame); - --frameCount; - } else { - frame = null; - } - } - - // visits the instruction at this offset - int opcode = b[u] & 0xFF; - switch (ClassWriter.TYPE[opcode]) { - case ClassWriter.NOARG_INSN: - mv.visitInsn(opcode); - u += 1; - break; - case ClassWriter.IMPLVAR_INSN: - if (opcode > Opcodes.ISTORE) { - opcode -= 59; // ISTORE_0 - mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), - opcode & 0x3); - } else { - opcode -= 26; // ILOAD_0 - mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); - } - u += 1; - break; - case ClassWriter.LABEL_INSN: - mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]); - u += 3; - break; - case ClassWriter.LABELW_INSN: - mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]); - u += 5; - break; - case ClassWriter.WIDE_INSN: - opcode = b[u + 1] & 0xFF; - if (opcode == Opcodes.IINC) { - mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4)); - u += 6; - } else { - mv.visitVarInsn(opcode, readUnsignedShort(u + 2)); - u += 4; - } - break; - case ClassWriter.TABL_INSN: { - // skips 0 to 3 padding bytes - u = u + 4 - (offset & 3); - // reads instruction - int label = offset + readInt(u); - int min = readInt(u + 4); - int max = readInt(u + 8); - Label[] table = new Label[max - min + 1]; - u += 12; - for (int i = 0; i < table.length; ++i) { - table[i] = labels[offset + readInt(u)]; - u += 4; - } - mv.visitTableSwitchInsn(min, max, labels[label], table); - break; - } - case ClassWriter.LOOK_INSN: { - // skips 0 to 3 padding bytes - u = u + 4 - (offset & 3); - // reads instruction - int label = offset + readInt(u); - int len = readInt(u + 4); - int[] keys = new int[len]; - Label[] values = new Label[len]; - u += 8; - for (int i = 0; i < len; ++i) { - keys[i] = readInt(u); - values[i] = labels[offset + readInt(u + 4)]; - u += 8; - } - mv.visitLookupSwitchInsn(labels[label], keys, values); - break; - } - case ClassWriter.VAR_INSN: - mv.visitVarInsn(opcode, b[u + 1] & 0xFF); - u += 2; - break; - case ClassWriter.SBYTE_INSN: - mv.visitIntInsn(opcode, b[u + 1]); - u += 2; - break; - case ClassWriter.SHORT_INSN: - mv.visitIntInsn(opcode, readShort(u + 1)); - u += 3; - break; - case ClassWriter.LDC_INSN: - mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c)); - u += 2; - break; - case ClassWriter.LDCW_INSN: - mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c)); - u += 3; - break; - case ClassWriter.FIELDORMETH_INSN: - case ClassWriter.ITFMETH_INSN: { - int cpIndex = items[readUnsignedShort(u + 1)]; - boolean itf = b[cpIndex - 1] == ClassWriter.IMETH; - String iowner = readClass(cpIndex, c); - cpIndex = items[readUnsignedShort(cpIndex + 2)]; - String iname = readUTF8(cpIndex, c); - String idesc = readUTF8(cpIndex + 2, c); - if (opcode < Opcodes.INVOKEVIRTUAL) { - mv.visitFieldInsn(opcode, iowner, iname, idesc); - } else { - mv.visitMethodInsn(opcode, iowner, iname, idesc, itf); - } - if (opcode == Opcodes.INVOKEINTERFACE) { - u += 5; - } else { - u += 3; - } - break; - } - case ClassWriter.INDYMETH_INSN: { - int cpIndex = items[readUnsignedShort(u + 1)]; - int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)]; - Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c); - int bsmArgCount = readUnsignedShort(bsmIndex + 2); - Object[] bsmArgs = new Object[bsmArgCount]; - bsmIndex += 4; - for (int i = 0; i < bsmArgCount; i++) { - bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c); - bsmIndex += 2; - } - cpIndex = items[readUnsignedShort(cpIndex + 2)]; - String iname = readUTF8(cpIndex, c); - String idesc = readUTF8(cpIndex + 2, c); - mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs); - u += 5; - break; - } - case ClassWriter.TYPE_INSN: - mv.visitTypeInsn(opcode, readClass(u + 1, c)); - u += 3; - break; - case ClassWriter.IINC_INSN: - mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]); - u += 3; - break; - // case MANA_INSN: - default: - mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF); - u += 4; - break; - } - - // visit the instruction annotations, if any - while (tanns != null && tann < tanns.length && ntoff <= offset) { - if (ntoff == offset) { - int v = readAnnotationTarget(context, tanns[tann]); - readAnnotationValues(v + 2, c, true, - mv.visitInsnAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), true)); - } - ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1 - : readUnsignedShort(tanns[tann] + 1); - } - while (itanns != null && itann < itanns.length && nitoff <= offset) { - if (nitoff == offset) { - int v = readAnnotationTarget(context, itanns[itann]); - readAnnotationValues(v + 2, c, true, - mv.visitInsnAnnotation(context.typeRef, - context.typePath, readUTF8(v, c), false)); - } - nitoff = ++itann >= itanns.length - || readByte(itanns[itann]) < 0x43 ? -1 - : readUnsignedShort(itanns[itann] + 1); - } - } - if (labels[codeLength] != null) { - mv.visitLabel(labels[codeLength]); - } - - // visits the local variable tables - if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) { - int[] typeTable = null; - if (varTypeTable != 0) { - u = varTypeTable + 2; - typeTable = new int[readUnsignedShort(varTypeTable) * 3]; - for (int i = typeTable.length; i > 0;) { - typeTable[--i] = u + 6; // signature - typeTable[--i] = readUnsignedShort(u + 8); // index - typeTable[--i] = readUnsignedShort(u); // start - u += 10; - } - } - u = varTable + 2; - for (int i = readUnsignedShort(varTable); i > 0; --i) { - int start = readUnsignedShort(u); - int length = readUnsignedShort(u + 2); - int index = readUnsignedShort(u + 8); - String vsignature = null; - if (typeTable != null) { - for (int j = 0; j < typeTable.length; j += 3) { - if (typeTable[j] == start && typeTable[j + 1] == index) { - vsignature = readUTF8(typeTable[j + 2], c); - break; - } - } - } - mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c), - vsignature, labels[start], labels[start + length], - index); - u += 10; - } - } - - // visits the local variables type annotations - if (tanns != null) { - for (int i = 0; i < tanns.length; ++i) { - if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) { - int v = readAnnotationTarget(context, tanns[i]); - v = readAnnotationValues(v + 2, c, true, - mv.visitLocalVariableAnnotation(context.typeRef, - context.typePath, context.start, - context.end, context.index, readUTF8(v, c), - true)); - } - } - } - if (itanns != null) { - for (int i = 0; i < itanns.length; ++i) { - if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) { - int v = readAnnotationTarget(context, itanns[i]); - v = readAnnotationValues(v + 2, c, true, - mv.visitLocalVariableAnnotation(context.typeRef, - context.typePath, context.start, - context.end, context.index, readUTF8(v, c), - false)); - } - } - } - - // visits the code attributes - while (attributes != null) { - Attribute attr = attributes.next; - attributes.next = null; - mv.visitAttribute(attributes); - attributes = attr; - } - - // visits the max stack and max locals values - mv.visitMaxs(maxStack, maxLocals); - } - - /** - * Parses a type annotation table to find the labels, and to visit the try - * catch block annotations. - * - * @param u - * the start offset of a type annotation table. - * @param mv - * the method visitor to be used to visit the try catch block - * annotations. - * @param context - * information about the class being parsed. - * @param visible - * if the type annotation table to parse contains runtime visible - * annotations. - * @return the start offset of each type annotation in the parsed table. - */ - private int[] readTypeAnnotations(final MethodVisitor mv, - final Context context, int u, boolean visible) { - char[] c = context.buffer; - int[] offsets = new int[readUnsignedShort(u)]; - u += 2; - for (int i = 0; i < offsets.length; ++i) { - offsets[i] = u; - int target = readInt(u); - switch (target >>> 24) { - case 0x00: // CLASS_TYPE_PARAMETER - case 0x01: // METHOD_TYPE_PARAMETER - case 0x16: // METHOD_FORMAL_PARAMETER - u += 2; - break; - case 0x13: // FIELD - case 0x14: // METHOD_RETURN - case 0x15: // METHOD_RECEIVER - u += 1; - break; - case 0x40: // LOCAL_VARIABLE - case 0x41: // RESOURCE_VARIABLE - for (int j = readUnsignedShort(u + 1); j > 0; --j) { - int start = readUnsignedShort(u + 3); - int length = readUnsignedShort(u + 5); - readLabel(start, context.labels); - readLabel(start + length, context.labels); - u += 6; - } - u += 3; - break; - case 0x47: // CAST - case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT - case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT - case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT - case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT - u += 4; - break; - // case 0x10: // CLASS_EXTENDS - // case 0x11: // CLASS_TYPE_PARAMETER_BOUND - // case 0x12: // METHOD_TYPE_PARAMETER_BOUND - // case 0x17: // THROWS - // case 0x42: // EXCEPTION_PARAMETER - // case 0x43: // INSTANCEOF - // case 0x44: // NEW - // case 0x45: // CONSTRUCTOR_REFERENCE - // case 0x46: // METHOD_REFERENCE - default: - u += 3; - break; - } - int pathLength = readByte(u); - if ((target >>> 24) == 0x42) { - TypePath path = pathLength == 0 ? null : new TypePath(b, u); - u += 1 + 2 * pathLength; - u = readAnnotationValues(u + 2, c, true, - mv.visitTryCatchAnnotation(target, path, - readUTF8(u, c), visible)); - } else { - u = readAnnotationValues(u + 3 + 2 * pathLength, c, true, null); - } - } - return offsets; - } - - /** - * Parses the header of a type annotation to extract its target_type and - * target_path (the result is stored in the given context), and returns the - * start offset of the rest of the type_annotation structure (i.e. the - * offset to the type_index field, which is followed by - * num_element_value_pairs and then the name,value pairs). - * - * @param context - * information about the class being parsed. This is where the - * extracted target_type and target_path must be stored. - * @param u - * the start offset of a type_annotation structure. - * @return the start offset of the rest of the type_annotation structure. - */ - private int readAnnotationTarget(final Context context, int u) { - int target = readInt(u); - switch (target >>> 24) { - case 0x00: // CLASS_TYPE_PARAMETER - case 0x01: // METHOD_TYPE_PARAMETER - case 0x16: // METHOD_FORMAL_PARAMETER - target &= 0xFFFF0000; - u += 2; - break; - case 0x13: // FIELD - case 0x14: // METHOD_RETURN - case 0x15: // METHOD_RECEIVER - target &= 0xFF000000; - u += 1; - break; - case 0x40: // LOCAL_VARIABLE - case 0x41: { // RESOURCE_VARIABLE - target &= 0xFF000000; - int n = readUnsignedShort(u + 1); - context.start = new Label[n]; - context.end = new Label[n]; - context.index = new int[n]; - u += 3; - for (int i = 0; i < n; ++i) { - int start = readUnsignedShort(u); - int length = readUnsignedShort(u + 2); - context.start[i] = readLabel(start, context.labels); - context.end[i] = readLabel(start + length, context.labels); - context.index[i] = readUnsignedShort(u + 4); - u += 6; - } - break; - } - case 0x47: // CAST - case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT - case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT - case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT - case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT - target &= 0xFF0000FF; - u += 4; - break; - // case 0x10: // CLASS_EXTENDS - // case 0x11: // CLASS_TYPE_PARAMETER_BOUND - // case 0x12: // METHOD_TYPE_PARAMETER_BOUND - // case 0x17: // THROWS - // case 0x42: // EXCEPTION_PARAMETER - // case 0x43: // INSTANCEOF - // case 0x44: // NEW - // case 0x45: // CONSTRUCTOR_REFERENCE - // case 0x46: // METHOD_REFERENCE - default: - target &= (target >>> 24) < 0x43 ? 0xFFFFFF00 : 0xFF000000; - u += 3; - break; - } - int pathLength = readByte(u); - context.typeRef = target; - context.typePath = pathLength == 0 ? null : new TypePath(b, u); - return u + 1 + 2 * pathLength; - } - - /** - * Reads parameter annotations and makes the given visitor visit them. - * - * @param mv - * the visitor that must visit the annotations. - * @param context - * information about the class being parsed. - * @param v - * start offset in {@link #b b} of the annotations to be read. - * @param visible - * true if the annotations to be read are visible at - * runtime. - */ - private void readParameterAnnotations(final MethodVisitor mv, - final Context context, int v, final boolean visible) { - int i; - int n = b[v++] & 0xFF; - // workaround for a bug in javac (javac compiler generates a parameter - // annotation array whose size is equal to the number of parameters in - // the Java source file, while it should generate an array whose size is - // equal to the number of parameters in the method descriptor - which - // includes the synthetic parameters added by the compiler). This work- - // around supposes that the synthetic parameters are the first ones. - int synthetics = Type.getArgumentTypes(context.desc).length - n; - AnnotationVisitor av; - for (i = 0; i < synthetics; ++i) { - // virtual annotation to detect synthetic parameters in MethodWriter - av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false); - if (av != null) { - av.visitEnd(); - } - } - char[] c = context.buffer; - for (; i < n + synthetics; ++i) { - int j = readUnsignedShort(v); - v += 2; - for (; j > 0; --j) { - av = mv.visitParameterAnnotation(i, readUTF8(v, c), visible); - v = readAnnotationValues(v + 2, c, true, av); - } - } - } - - /** - * Reads the values of an annotation and makes the given visitor visit them. - * - * @param v - * the start offset in {@link #b b} of the values to be read - * (including the unsigned short that gives the number of - * values). - * @param buf - * buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or {@link #readConst - * readConst}. - * @param named - * if the annotation values are named or not. - * @param av - * the visitor that must visit the values. - * @return the end offset of the annotation values. - */ - private int readAnnotationValues(int v, final char[] buf, - final boolean named, final AnnotationVisitor av) { - int i = readUnsignedShort(v); - v += 2; - if (named) { - for (; i > 0; --i) { - v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); - } - } else { - for (; i > 0; --i) { - v = readAnnotationValue(v, buf, null, av); - } - } - if (av != null) { - av.visitEnd(); - } - return v; - } - - /** - * Reads a value of an annotation and makes the given visitor visit it. - * - * @param v - * the start offset in {@link #b b} of the value to be read - * (not including the value name constant pool index). - * @param buf - * buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or {@link #readConst - * readConst}. - * @param name - * the name of the value to be read. - * @param av - * the visitor that must visit the value. - * @return the end offset of the annotation value. - */ - private int readAnnotationValue(int v, final char[] buf, final String name, - final AnnotationVisitor av) { - int i; - if (av == null) { - switch (b[v] & 0xFF) { - case 'e': // enum_const_value - return v + 5; - case '@': // annotation_value - return readAnnotationValues(v + 3, buf, true, null); - case '[': // array_value - return readAnnotationValues(v + 1, buf, false, null); - default: - return v + 3; - } - } - switch (b[v++] & 0xFF) { - case 'I': // pointer to CONSTANT_Integer - case 'J': // pointer to CONSTANT_Long - case 'F': // pointer to CONSTANT_Float - case 'D': // pointer to CONSTANT_Double - av.visit(name, readConst(readUnsignedShort(v), buf)); - v += 2; - break; - case 'B': // pointer to CONSTANT_Byte - av.visit(name, (byte) readInt(items[readUnsignedShort(v)])); - v += 2; - break; - case 'Z': // pointer to CONSTANT_Boolean - av.visit(name, - readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE - : Boolean.TRUE); - v += 2; - break; - case 'S': // pointer to CONSTANT_Short - av.visit(name, (short) readInt(items[readUnsignedShort(v)])); - v += 2; - break; - case 'C': // pointer to CONSTANT_Char - av.visit(name, (char) readInt(items[readUnsignedShort(v)])); - v += 2; - break; - case 's': // pointer to CONSTANT_Utf8 - av.visit(name, readUTF8(v, buf)); - v += 2; - break; - case 'e': // enum_const_value - av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); - v += 4; - break; - case 'c': // class_info - av.visit(name, Type.getType(readUTF8(v, buf))); - v += 2; - break; - case '@': // annotation_value - v = readAnnotationValues(v + 2, buf, true, - av.visitAnnotation(name, readUTF8(v, buf))); - break; - case '[': // array_value - int size = readUnsignedShort(v); - v += 2; - if (size == 0) { - return readAnnotationValues(v - 2, buf, false, - av.visitArray(name)); - } - switch (this.b[v++] & 0xFF) { - case 'B': - byte[] bv = new byte[size]; - for (i = 0; i < size; i++) { - bv[i] = (byte) readInt(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, bv); - --v; - break; - case 'Z': - boolean[] zv = new boolean[size]; - for (i = 0; i < size; i++) { - zv[i] = readInt(items[readUnsignedShort(v)]) != 0; - v += 3; - } - av.visit(name, zv); - --v; - break; - case 'S': - short[] sv = new short[size]; - for (i = 0; i < size; i++) { - sv[i] = (short) readInt(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, sv); - --v; - break; - case 'C': - char[] cv = new char[size]; - for (i = 0; i < size; i++) { - cv[i] = (char) readInt(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, cv); - --v; - break; - case 'I': - int[] iv = new int[size]; - for (i = 0; i < size; i++) { - iv[i] = readInt(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, iv); - --v; - break; - case 'J': - long[] lv = new long[size]; - for (i = 0; i < size; i++) { - lv[i] = readLong(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, lv); - --v; - break; - case 'F': - float[] fv = new float[size]; - for (i = 0; i < size; i++) { - fv[i] = Float - .intBitsToFloat(readInt(items[readUnsignedShort(v)])); - v += 3; - } - av.visit(name, fv); - --v; - break; - case 'D': - double[] dv = new double[size]; - for (i = 0; i < size; i++) { - dv[i] = Double - .longBitsToDouble(readLong(items[readUnsignedShort(v)])); - v += 3; - } - av.visit(name, dv); - --v; - break; - default: - v = readAnnotationValues(v - 3, buf, false, av.visitArray(name)); - } - } - return v; - } - - /** - * Computes the implicit frame of the method currently being parsed (as - * defined in the given {@link Context}) and stores it in the given context. - * - * @param frame - * information about the class being parsed. - */ - private void getImplicitFrame(final Context frame) { - String desc = frame.desc; - Object[] locals = frame.local; - int local = 0; - if ((frame.access & Opcodes.ACC_STATIC) == 0) { - if ("".equals(frame.name)) { - locals[local++] = Opcodes.UNINITIALIZED_THIS; - } else { - locals[local++] = readClass(header + 2, frame.buffer); - } - } - int i = 1; - loop: while (true) { - int j = i; - switch (desc.charAt(i++)) { - case 'Z': - case 'C': - case 'B': - case 'S': - case 'I': - locals[local++] = Opcodes.INTEGER; - break; - case 'F': - locals[local++] = Opcodes.FLOAT; - break; - case 'J': - locals[local++] = Opcodes.LONG; - break; - case 'D': - locals[local++] = Opcodes.DOUBLE; - break; - case '[': - while (desc.charAt(i) == '[') { - ++i; - } - if (desc.charAt(i) == 'L') { - ++i; - while (desc.charAt(i) != ';') { - ++i; - } - } - locals[local++] = desc.substring(j, ++i); - break; - case 'L': - while (desc.charAt(i) != ';') { - ++i; - } - locals[local++] = desc.substring(j + 1, i++); - break; - default: - break loop; - } - } - frame.localCount = local; - } - - /** - * Reads a stack map frame and stores the result in the given - * {@link Context} object. - * - * @param stackMap - * the start offset of a stack map frame in the class file. - * @param zip - * if the stack map frame at stackMap is compressed or not. - * @param unzip - * if the stack map frame must be uncompressed. - * @param frame - * where the parsed stack map frame must be stored. - * @return the offset of the first byte following the parsed frame. - */ - private int readFrame(int stackMap, boolean zip, boolean unzip, - Context frame) { - char[] c = frame.buffer; - Label[] labels = frame.labels; - int tag; - int delta; - if (zip) { - tag = b[stackMap++] & 0xFF; - } else { - tag = MethodWriter.FULL_FRAME; - frame.offset = -1; - } - frame.localDiff = 0; - if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) { - delta = tag; - frame.mode = Opcodes.F_SAME; - frame.stackCount = 0; - } else if (tag < MethodWriter.RESERVED) { - delta = tag - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; - stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); - frame.mode = Opcodes.F_SAME1; - frame.stackCount = 1; - } else { - delta = readUnsignedShort(stackMap); - stackMap += 2; - if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { - stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); - frame.mode = Opcodes.F_SAME1; - frame.stackCount = 1; - } else if (tag >= MethodWriter.CHOP_FRAME - && tag < MethodWriter.SAME_FRAME_EXTENDED) { - frame.mode = Opcodes.F_CHOP; - frame.localDiff = MethodWriter.SAME_FRAME_EXTENDED - tag; - frame.localCount -= frame.localDiff; - frame.stackCount = 0; - } else if (tag == MethodWriter.SAME_FRAME_EXTENDED) { - frame.mode = Opcodes.F_SAME; - frame.stackCount = 0; - } else if (tag < MethodWriter.FULL_FRAME) { - int local = unzip ? frame.localCount : 0; - for (int i = tag - MethodWriter.SAME_FRAME_EXTENDED; i > 0; i--) { - stackMap = readFrameType(frame.local, local++, stackMap, c, - labels); - } - frame.mode = Opcodes.F_APPEND; - frame.localDiff = tag - MethodWriter.SAME_FRAME_EXTENDED; - frame.localCount += frame.localDiff; - frame.stackCount = 0; - } else { // if (tag == FULL_FRAME) { - frame.mode = Opcodes.F_FULL; - int n = readUnsignedShort(stackMap); - stackMap += 2; - frame.localDiff = n; - frame.localCount = n; - for (int local = 0; n > 0; n--) { - stackMap = readFrameType(frame.local, local++, stackMap, c, - labels); - } - n = readUnsignedShort(stackMap); - stackMap += 2; - frame.stackCount = n; - for (int stack = 0; n > 0; n--) { - stackMap = readFrameType(frame.stack, stack++, stackMap, c, - labels); - } - } - } - frame.offset += delta + 1; - readLabel(frame.offset, labels); - return stackMap; - } - - /** - * Reads a stack map frame type and stores it at the given index in the - * given array. - * - * @param frame - * the array where the parsed type must be stored. - * @param index - * the index in 'frame' where the parsed type must be stored. - * @param v - * the start offset of the stack map frame type to read. - * @param buf - * a buffer to read strings. - * @param labels - * the labels of the method currently being parsed, indexed by - * their offset. If the parsed type is an Uninitialized type, a - * new label for the corresponding NEW instruction is stored in - * this array if it does not already exist. - * @return the offset of the first byte after the parsed type. - */ - private int readFrameType(final Object[] frame, final int index, int v, - final char[] buf, final Label[] labels) { - int type = b[v++] & 0xFF; - switch (type) { - case 0: - frame[index] = Opcodes.TOP; - break; - case 1: - frame[index] = Opcodes.INTEGER; - break; - case 2: - frame[index] = Opcodes.FLOAT; - break; - case 3: - frame[index] = Opcodes.DOUBLE; - break; - case 4: - frame[index] = Opcodes.LONG; - break; - case 5: - frame[index] = Opcodes.NULL; - break; - case 6: - frame[index] = Opcodes.UNINITIALIZED_THIS; - break; - case 7: // Object - frame[index] = readClass(v, buf); - v += 2; - break; - default: // Uninitialized - frame[index] = readLabel(readUnsignedShort(v), labels); - v += 2; - } - return v; - } - - /** - * Returns the label corresponding to the given offset. The default - * implementation of this method creates a label for the given offset if it - * has not been already created. - * - * @param offset - * a bytecode offset in a method. - * @param labels - * the already created labels, indexed by their offset. If a - * label already exists for offset this method must not create a - * new one. Otherwise it must store the new label in this array. - * @return a non null Label, which must be equal to labels[offset]. - */ - protected Label readLabel(int offset, Label[] labels) { - if (labels[offset] == null) { - labels[offset] = new Label(); - } - return labels[offset]; - } - - /** - * Returns the start index of the attribute_info structure of this class. - * - * @return the start index of the attribute_info structure of this class. - */ - private int getAttributes() { - // skips the header - int u = header + 8 + readUnsignedShort(header + 6) * 2; - // skips fields and methods - for (int i = readUnsignedShort(u); i > 0; --i) { - for (int j = readUnsignedShort(u + 8); j > 0; --j) { - u += 6 + readInt(u + 12); - } - u += 8; - } - u += 2; - for (int i = readUnsignedShort(u); i > 0; --i) { - for (int j = readUnsignedShort(u + 8); j > 0; --j) { - u += 6 + readInt(u + 12); - } - u += 8; - } - // the attribute_info structure starts just after the methods - return u + 2; - } - - /** - * Reads an attribute in {@link #b b}. - * - * @param attrs - * prototypes of the attributes that must be parsed during the - * visit of the class. Any attribute whose type is not equal to - * the type of one the prototypes is ignored (i.e. an empty - * {@link Attribute} instance is returned). - * @param type - * the type of the attribute. - * @param off - * index of the first byte of the attribute's content in - * {@link #b b}. The 6 attribute header bytes, containing the - * type and the length of the attribute, are not taken into - * account here (they have already been read). - * @param len - * the length of the attribute's content. - * @param buf - * buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or {@link #readConst - * readConst}. - * @param codeOff - * index of the first byte of code's attribute content in - * {@link #b b}, or -1 if the attribute to be read is not a code - * attribute. The 6 attribute header bytes, containing the type - * and the length of the attribute, are not taken into account - * here. - * @param labels - * the labels of the method's code, or null if the - * attribute to be read is not a code attribute. - * @return the attribute that has been read, or null to skip this - * attribute. - */ - private Attribute readAttribute(final Attribute[] attrs, final String type, - final int off, final int len, final char[] buf, final int codeOff, - final Label[] labels) { - for (int i = 0; i < attrs.length; ++i) { - if (attrs[i].type.equals(type)) { - return attrs[i].read(this, off, len, buf, codeOff, labels); - } - } - return new Attribute(type).read(this, off, len, null, -1, null); - } - - // ------------------------------------------------------------------------ - // Utility methods: low level parsing - // ------------------------------------------------------------------------ - - /** - * Returns the number of constant pool items in {@link #b b}. - * - * @return the number of constant pool items in {@link #b b}. - */ - public int getItemCount() { - return items.length; - } - - /** - * Returns the start index of the constant pool item in {@link #b b}, plus - * one. This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param item - * the index a constant pool item. - * @return the start index of the constant pool item in {@link #b b}, plus - * one. - */ - public int getItem(final int item) { - return items[item]; - } - - /** - * Returns the maximum length of the strings contained in the constant pool - * of the class. - * - * @return the maximum length of the strings contained in the constant pool - * of the class. - */ - public int getMaxStringLength() { - return maxStringLength; - } - - /** - * Reads a byte value in {@link #b b}. This method is intended for - * {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index - * the start index of the value to be read in {@link #b b}. - * @return the read value. - */ - public int readByte(final int index) { - return b[index] & 0xFF; - } - - /** - * Reads an unsigned short value in {@link #b b}. This method is intended - * for {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index - * the start index of the value to be read in {@link #b b}. - * @return the read value. - */ - public int readUnsignedShort(final int index) { - byte[] b = this.b; - return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); - } - - /** - * Reads a signed short value in {@link #b b}. This method is intended - * for {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index - * the start index of the value to be read in {@link #b b}. - * @return the read value. - */ - public short readShort(final int index) { - byte[] b = this.b; - return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); - } - - /** - * Reads a signed int value in {@link #b b}. This method is intended for - * {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index - * the start index of the value to be read in {@link #b b}. - * @return the read value. - */ - public int readInt(final int index) { - byte[] b = this.b; - return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) - | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); - } - - /** - * Reads a signed long value in {@link #b b}. This method is intended for - * {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index - * the start index of the value to be read in {@link #b b}. - * @return the read value. - */ - public long readLong(final int index) { - long l1 = readInt(index); - long l0 = readInt(index + 4) & 0xFFFFFFFFL; - return (l1 << 32) | l0; - } - - /** - * Reads an UTF8 string constant pool item in {@link #b b}. This method - * is intended for {@link Attribute} sub classes, and is normally not needed - * by class generators or adapters. - * - * @param index - * the start index of an unsigned short value in {@link #b b}, - * whose value is the index of an UTF8 constant pool item. - * @param buf - * buffer to be used to read the item. This buffer must be - * sufficiently large. It is not automatically resized. - * @return the String corresponding to the specified UTF8 item. - */ - public String readUTF8(int index, final char[] buf) { - int item = readUnsignedShort(index); - if (index == 0 || item == 0) { - return null; - } - String s = strings[item]; - if (s != null) { - return s; - } - index = items[item]; - return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); - } - - /** - * Reads UTF8 string in {@link #b b}. - * - * @param index - * start offset of the UTF8 string to be read. - * @param utfLen - * length of the UTF8 string to be read. - * @param buf - * buffer to be used to read the string. This buffer must be - * sufficiently large. It is not automatically resized. - * @return the String corresponding to the specified UTF8 string. - */ - private String readUTF(int index, final int utfLen, final char[] buf) { - int endIndex = index + utfLen; - byte[] b = this.b; - int strLen = 0; - int c; - int st = 0; - char cc = 0; - while (index < endIndex) { - c = b[index++]; - switch (st) { - case 0: - c = c & 0xFF; - if (c < 0x80) { // 0xxxxxxx - buf[strLen++] = (char) c; - } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx - cc = (char) (c & 0x1F); - st = 1; - } else { // 1110 xxxx 10xx xxxx 10xx xxxx - cc = (char) (c & 0x0F); - st = 2; - } - break; - - case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char - buf[strLen++] = (char) ((cc << 6) | (c & 0x3F)); - st = 0; - break; - - case 2: // byte 2 of 3-byte char - cc = (char) ((cc << 6) | (c & 0x3F)); - st = 1; - break; - } - } - return new String(buf, 0, strLen); - } - - /** - * Reads a class constant pool item in {@link #b b}. This method is - * intended for {@link Attribute} sub classes, and is normally not needed by - * class generators or adapters. - * - * @param index - * the start index of an unsigned short value in {@link #b b}, - * whose value is the index of a class constant pool item. - * @param buf - * buffer to be used to read the item. This buffer must be - * sufficiently large. It is not automatically resized. - * @return the String corresponding to the specified class item. - */ - public String readClass(final int index, final char[] buf) { - // computes the start index of the CONSTANT_Class item in b - // and reads the CONSTANT_Utf8 item designated by - // the first two bytes of this CONSTANT_Class item - return readUTF8(items[readUnsignedShort(index)], buf); - } - - /** - * Reads a numeric or string constant pool item in {@link #b b}. This - * method is intended for {@link Attribute} sub classes, and is normally not - * needed by class generators or adapters. - * - * @param item - * the index of a constant pool item. - * @param buf - * buffer to be used to read the item. This buffer must be - * sufficiently large. It is not automatically resized. - * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, - * {@link String}, {@link Type} or {@link Handle} corresponding to - * the given constant pool item. - */ - public Object readConst(final int item, final char[] buf) { - int index = items[item]; - switch (b[index - 1]) { - case ClassWriter.INT: - return readInt(index); - case ClassWriter.FLOAT: - return Float.intBitsToFloat(readInt(index)); - case ClassWriter.LONG: - return readLong(index); - case ClassWriter.DOUBLE: - return Double.longBitsToDouble(readLong(index)); - case ClassWriter.CLASS: - return Type.getObjectType(readUTF8(index, buf)); - case ClassWriter.STR: - return readUTF8(index, buf); - case ClassWriter.MTYPE: - return Type.getMethodType(readUTF8(index, buf)); - default: // case ClassWriter.HANDLE_BASE + [1..9]: - int tag = readByte(index); - int[] items = this.items; - int cpIndex = items[readUnsignedShort(index + 1)]; - String owner = readClass(cpIndex, buf); - cpIndex = items[readUnsignedShort(cpIndex + 2)]; - String name = readUTF8(cpIndex, buf); - String desc = readUTF8(cpIndex + 2, buf); - return new Handle(tag, owner, name, desc); - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassVisitor.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassVisitor.java deleted file mode 100644 index c351c7dd..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassVisitor.java +++ /dev/null @@ -1,320 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A visitor to visit a Java class. The methods of this class must be called in - * the following order: visit [ visitSource ] [ - * visitOuterClass ] ( visitAnnotation | - * visitTypeAnnotation | visitAttribute )* ( - * visitInnerClass | visitField | visitMethod )* - * visitEnd. - * - * @author Eric Bruneton - */ -public abstract class ClassVisitor { - - /** - * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - */ - protected final int api; - - /** - * The class visitor to which this visitor must delegate method calls. May - * be null. - */ - protected ClassVisitor cv; - - /** - * Constructs a new {@link ClassVisitor}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - */ - public ClassVisitor(final int api) { - this(api, null); - } - - /** - * Constructs a new {@link ClassVisitor}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - * @param cv - * the class visitor to which this visitor must delegate method - * calls. May be null. - */ - public ClassVisitor(final int api, final ClassVisitor cv) { - if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { - throw new IllegalArgumentException(); - } - this.api = api; - this.cv = cv; - } - - /** - * Visits the header of the class. - * - * @param version - * the class version. - * @param access - * the class's access flags (see {@link Opcodes}). This parameter - * also indicates if the class is deprecated. - * @param name - * the internal name of the class (see - * {@link Type#getInternalName() getInternalName}). - * @param signature - * the signature of this class. May be null if the class - * is not a generic one, and does not extend or implement generic - * classes or interfaces. - * @param superName - * the internal of name of the super class (see - * {@link Type#getInternalName() getInternalName}). For - * interfaces, the super class is {@link Object}. May be - * null, but only for the {@link Object} class. - * @param interfaces - * the internal names of the class's interfaces (see - * {@link Type#getInternalName() getInternalName}). May be - * null. - */ - public void visit(int version, int access, String name, String signature, - String superName, String[] interfaces) { - if (cv != null) { - cv.visit(version, access, name, signature, superName, interfaces); - } - } - - /** - * Visits the source of the class. - * - * @param source - * the name of the source file from which the class was compiled. - * May be null. - * @param debug - * additional debug information to compute the correspondance - * between source and compiled elements of the class. May be - * null. - */ - public void visitSource(String source, String debug) { - if (cv != null) { - cv.visitSource(source, debug); - } - } - - /** - * Visits the enclosing class of the class. This method must be called only - * if the class has an enclosing class. - * - * @param owner - * internal name of the enclosing class of the class. - * @param name - * the name of the method that contains the class, or - * null if the class is not enclosed in a method of its - * enclosing class. - * @param desc - * the descriptor of the method that contains the class, or - * null if the class is not enclosed in a method of its - * enclosing class. - */ - public void visitOuterClass(String owner, String name, String desc) { - if (cv != null) { - cv.visitOuterClass(owner, name, desc); - } - } - - /** - * Visits an annotation of the class. - * - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (cv != null) { - return cv.visitAnnotation(desc, visible); - } - return null; - } - - /** - * Visits an annotation on a type in the class signature. - * - * @param typeRef - * a reference to the annotated type. The sort of this type - * reference must be {@link TypeReference#CLASS_TYPE_PARAMETER - * CLASS_TYPE_PARAMETER}, - * {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND - * CLASS_TYPE_PARAMETER_BOUND} or - * {@link TypeReference#CLASS_EXTENDS CLASS_EXTENDS}. See - * {@link TypeReference}. - * @param typePath - * the path to the annotated type argument, wildcard bound, array - * element type, or static inner type within 'typeRef'. May be - * null if the annotation targets 'typeRef' as a whole. - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitTypeAnnotation(int typeRef, - TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } - if (cv != null) { - return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); - } - return null; - } - - /** - * Visits a non standard attribute of the class. - * - * @param attr - * an attribute. - */ - public void visitAttribute(Attribute attr) { - if (cv != null) { - cv.visitAttribute(attr); - } - } - - /** - * Visits information about an inner class. This inner class is not - * necessarily a member of the class being visited. - * - * @param name - * the internal name of an inner class (see - * {@link Type#getInternalName() getInternalName}). - * @param outerName - * the internal name of the class to which the inner class - * belongs (see {@link Type#getInternalName() getInternalName}). - * May be null for not member classes. - * @param innerName - * the (simple) name of the inner class inside its enclosing - * class. May be null for anonymous inner classes. - * @param access - * the access flags of the inner class as originally declared in - * the enclosing class. - */ - public void visitInnerClass(String name, String outerName, - String innerName, int access) { - if (cv != null) { - cv.visitInnerClass(name, outerName, innerName, access); - } - } - - /** - * Visits a field of the class. - * - * @param access - * the field's access flags (see {@link Opcodes}). This parameter - * also indicates if the field is synthetic and/or deprecated. - * @param name - * the field's name. - * @param desc - * the field's descriptor (see {@link Type Type}). - * @param signature - * the field's signature. May be null if the field's - * type does not use generic types. - * @param value - * the field's initial value. This parameter, which may be - * null if the field does not have an initial value, - * must be an {@link Integer}, a {@link Float}, a {@link Long}, a - * {@link Double} or a {@link String} (for int, - * float, long or String fields - * respectively). This parameter is only used for static - * fields. Its value is ignored for non static fields, which - * must be initialized through bytecode instructions in - * constructors or methods. - * @return a visitor to visit field annotations and attributes, or - * null if this class visitor is not interested in visiting - * these annotations and attributes. - */ - public FieldVisitor visitField(int access, String name, String desc, - String signature, Object value) { - if (cv != null) { - return cv.visitField(access, name, desc, signature, value); - } - return null; - } - - /** - * Visits a method of the class. This method must return a new - * {@link MethodVisitor} instance (or null) each time it is called, - * i.e., it should not return a previously returned visitor. - * - * @param access - * the method's access flags (see {@link Opcodes}). This - * parameter also indicates if the method is synthetic and/or - * deprecated. - * @param name - * the method's name. - * @param desc - * the method's descriptor (see {@link Type Type}). - * @param signature - * the method's signature. May be null if the method - * parameters, return type and exceptions do not use generic - * types. - * @param exceptions - * the internal names of the method's exception classes (see - * {@link Type#getInternalName() getInternalName}). May be - * null. - * @return an object to visit the byte code of the method, or null - * if this class visitor is not interested in visiting the code of - * this method. - */ - public MethodVisitor visitMethod(int access, String name, String desc, - String signature, String[] exceptions) { - if (cv != null) { - return cv.visitMethod(access, name, desc, signature, exceptions); - } - return null; - } - - /** - * Visits the end of the class. This method, which is the last one to be - * called, is used to inform the visitor that all the fields and methods of - * the class have been visited. - */ - public void visitEnd() { - if (cv != null) { - cv.visitEnd(); - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassWriter.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassWriter.java deleted file mode 100644 index d1482632..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/ClassWriter.java +++ /dev/null @@ -1,1776 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A {@link ClassVisitor} that generates classes in bytecode form. More - * precisely this visitor generates a byte array conforming to the Java class - * file format. It can be used alone, to generate a Java class "from scratch", - * or with one or more {@link ClassReader ClassReader} and adapter class visitor - * to generate a modified class from one or more existing Java classes. - * - * @author Eric Bruneton - */ -public class ClassWriter extends ClassVisitor { - - /** - * Flag to automatically compute the maximum stack size and the maximum - * number of local variables of methods. If this flag is set, then the - * arguments of the {@link MethodVisitor#visitMaxs visitMaxs} method of the - * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod} - * method will be ignored, and computed automatically from the signature and - * the bytecode of each method. - * - * @see #ClassWriter(int) - */ - public static final int COMPUTE_MAXS = 1; - - /** - * Flag to automatically compute the stack map frames of methods from - * scratch. If this flag is set, then the calls to the - * {@link MethodVisitor#visitFrame} method are ignored, and the stack map - * frames are recomputed from the methods bytecode. The arguments of the - * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and - * recomputed from the bytecode. In other words, computeFrames implies - * computeMaxs. - * - * @see #ClassWriter(int) - */ - public static final int COMPUTE_FRAMES = 2; - - /** - * Pseudo access flag to distinguish between the synthetic attribute and the - * synthetic access flag. - */ - static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000; - - /** - * Factor to convert from ACC_SYNTHETIC_ATTRIBUTE to Opcode.ACC_SYNTHETIC. - */ - static final int TO_ACC_SYNTHETIC = ACC_SYNTHETIC_ATTRIBUTE - / Opcodes.ACC_SYNTHETIC; - - /** - * The type of instructions without any argument. - */ - static final int NOARG_INSN = 0; - - /** - * The type of instructions with an signed byte argument. - */ - static final int SBYTE_INSN = 1; - - /** - * The type of instructions with an signed short argument. - */ - static final int SHORT_INSN = 2; - - /** - * The type of instructions with a local variable index argument. - */ - static final int VAR_INSN = 3; - - /** - * The type of instructions with an implicit local variable index argument. - */ - static final int IMPLVAR_INSN = 4; - - /** - * The type of instructions with a type descriptor argument. - */ - static final int TYPE_INSN = 5; - - /** - * The type of field and method invocations instructions. - */ - static final int FIELDORMETH_INSN = 6; - - /** - * The type of the INVOKEINTERFACE/INVOKEDYNAMIC instruction. - */ - static final int ITFMETH_INSN = 7; - - /** - * The type of the INVOKEDYNAMIC instruction. - */ - static final int INDYMETH_INSN = 8; - - /** - * The type of instructions with a 2 bytes bytecode offset label. - */ - static final int LABEL_INSN = 9; - - /** - * The type of instructions with a 4 bytes bytecode offset label. - */ - static final int LABELW_INSN = 10; - - /** - * The type of the LDC instruction. - */ - static final int LDC_INSN = 11; - - /** - * The type of the LDC_W and LDC2_W instructions. - */ - static final int LDCW_INSN = 12; - - /** - * The type of the IINC instruction. - */ - static final int IINC_INSN = 13; - - /** - * The type of the TABLESWITCH instruction. - */ - static final int TABL_INSN = 14; - - /** - * The type of the LOOKUPSWITCH instruction. - */ - static final int LOOK_INSN = 15; - - /** - * The type of the MULTIANEWARRAY instruction. - */ - static final int MANA_INSN = 16; - - /** - * The type of the WIDE instruction. - */ - static final int WIDE_INSN = 17; - - /** - * The instruction types of all JVM opcodes. - */ - static final byte[] TYPE; - - /** - * The type of CONSTANT_Class constant pool items. - */ - static final int CLASS = 7; - - /** - * The type of CONSTANT_Fieldref constant pool items. - */ - static final int FIELD = 9; - - /** - * The type of CONSTANT_Methodref constant pool items. - */ - static final int METH = 10; - - /** - * The type of CONSTANT_InterfaceMethodref constant pool items. - */ - static final int IMETH = 11; - - /** - * The type of CONSTANT_String constant pool items. - */ - static final int STR = 8; - - /** - * The type of CONSTANT_Integer constant pool items. - */ - static final int INT = 3; - - /** - * The type of CONSTANT_Float constant pool items. - */ - static final int FLOAT = 4; - - /** - * The type of CONSTANT_Long constant pool items. - */ - static final int LONG = 5; - - /** - * The type of CONSTANT_Double constant pool items. - */ - static final int DOUBLE = 6; - - /** - * The type of CONSTANT_NameAndType constant pool items. - */ - static final int NAME_TYPE = 12; - - /** - * The type of CONSTANT_Utf8 constant pool items. - */ - static final int UTF8 = 1; - - /** - * The type of CONSTANT_MethodType constant pool items. - */ - static final int MTYPE = 16; - - /** - * The type of CONSTANT_MethodHandle constant pool items. - */ - static final int HANDLE = 15; - - /** - * The type of CONSTANT_InvokeDynamic constant pool items. - */ - static final int INDY = 18; - - /** - * The base value for all CONSTANT_MethodHandle constant pool items. - * Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9 - * different items. - */ - static final int HANDLE_BASE = 20; - - /** - * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, - * instead of the constant pool, in order to avoid clashes with normal - * constant pool items in the ClassWriter constant pool's hash table. - */ - static final int TYPE_NORMAL = 30; - - /** - * Uninitialized type Item stored in the ClassWriter - * {@link ClassWriter#typeTable}, instead of the constant pool, in order to - * avoid clashes with normal constant pool items in the ClassWriter constant - * pool's hash table. - */ - static final int TYPE_UNINIT = 31; - - /** - * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, - * instead of the constant pool, in order to avoid clashes with normal - * constant pool items in the ClassWriter constant pool's hash table. - */ - static final int TYPE_MERGED = 32; - - /** - * The type of BootstrapMethods items. These items are stored in a special - * class attribute named BootstrapMethods and not in the constant pool. - */ - static final int BSM = 33; - - /** - * The class reader from which this class writer was constructed, if any. - */ - ClassReader cr; - - /** - * Minor and major version numbers of the class to be generated. - */ - int version; - - /** - * Index of the next item to be added in the constant pool. - */ - int index; - - /** - * The constant pool of this class. - */ - final ByteVector pool; - - /** - * The constant pool's hash table data. - */ - Item[] items; - - /** - * The threshold of the constant pool's hash table. - */ - int threshold; - - /** - * A reusable key used to look for items in the {@link #items} hash table. - */ - final Item key; - - /** - * A reusable key used to look for items in the {@link #items} hash table. - */ - final Item key2; - - /** - * A reusable key used to look for items in the {@link #items} hash table. - */ - final Item key3; - - /** - * A reusable key used to look for items in the {@link #items} hash table. - */ - final Item key4; - - /** - * A type table used to temporarily store internal names that will not - * necessarily be stored in the constant pool. This type table is used by - * the control flow and data flow analysis algorithm used to compute stack - * map frames from scratch. This array associates to each index i - * the Item whose index is i. All Item objects stored in this array - * are also stored in the {@link #items} hash table. These two arrays allow - * to retrieve an Item from its index or, conversely, to get the index of an - * Item from its value. Each Item stores an internal name in its - * {@link Item#strVal1} field. - */ - Item[] typeTable; - - /** - * Number of elements in the {@link #typeTable} array. - */ - private short typeCount; - - /** - * The access flags of this class. - */ - private int access; - - /** - * The constant pool item that contains the internal name of this class. - */ - private int name; - - /** - * The internal name of this class. - */ - String thisName; - - /** - * The constant pool item that contains the signature of this class. - */ - private int signature; - - /** - * The constant pool item that contains the internal name of the super class - * of this class. - */ - private int superName; - - /** - * Number of interfaces implemented or extended by this class or interface. - */ - private int interfaceCount; - - /** - * The interfaces implemented or extended by this class or interface. More - * precisely, this array contains the indexes of the constant pool items - * that contain the internal names of these interfaces. - */ - private int[] interfaces; - - /** - * The index of the constant pool item that contains the name of the source - * file from which this class was compiled. - */ - private int sourceFile; - - /** - * The SourceDebug attribute of this class. - */ - private ByteVector sourceDebug; - - /** - * The constant pool item that contains the name of the enclosing class of - * this class. - */ - private int enclosingMethodOwner; - - /** - * The constant pool item that contains the name and descriptor of the - * enclosing method of this class. - */ - private int enclosingMethod; - - /** - * The runtime visible annotations of this class. - */ - private AnnotationWriter anns; - - /** - * The runtime invisible annotations of this class. - */ - private AnnotationWriter ianns; - - /** - * The runtime visible type annotations of this class. - */ - private AnnotationWriter tanns; - - /** - * The runtime invisible type annotations of this class. - */ - private AnnotationWriter itanns; - - /** - * The non standard attributes of this class. - */ - private Attribute attrs; - - /** - * The number of entries in the InnerClasses attribute. - */ - private int innerClassesCount; - - /** - * The InnerClasses attribute. - */ - private ByteVector innerClasses; - - /** - * The number of entries in the BootstrapMethods attribute. - */ - int bootstrapMethodsCount; - - /** - * The BootstrapMethods attribute. - */ - ByteVector bootstrapMethods; - - /** - * The fields of this class. These fields are stored in a linked list of - * {@link FieldWriter} objects, linked to each other by their - * {@link FieldWriter#fv} field. This field stores the first element of this - * list. - */ - FieldWriter firstField; - - /** - * The fields of this class. These fields are stored in a linked list of - * {@link FieldWriter} objects, linked to each other by their - * {@link FieldWriter#fv} field. This field stores the last element of this - * list. - */ - FieldWriter lastField; - - /** - * The methods of this class. These methods are stored in a linked list of - * {@link MethodWriter} objects, linked to each other by their - * {@link MethodWriter#mv} field. This field stores the first element of - * this list. - */ - MethodWriter firstMethod; - - /** - * The methods of this class. These methods are stored in a linked list of - * {@link MethodWriter} objects, linked to each other by their - * {@link MethodWriter#mv} field. This field stores the last element of this - * list. - */ - MethodWriter lastMethod; - - /** - * true if the maximum stack size and number of local variables - * must be automatically computed. - */ - private boolean computeMaxs; - - /** - * true if the stack map frames must be recomputed from scratch. - */ - private boolean computeFrames; - - /** - * true if the stack map tables of this class are invalid. The - * {@link MethodWriter#resizeInstructions} method cannot transform existing - * stack map tables, and so produces potentially invalid classes when it is - * executed. In this case the class is reread and rewritten with the - * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize - * stack map tables when this option is used). - */ - boolean invalidFrames; - - // ------------------------------------------------------------------------ - // Static initializer - // ------------------------------------------------------------------------ - - /** - * Computes the instruction types of JVM opcodes. - */ - static { - int i; - byte[] b = new byte[220]; - String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" - + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - + "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA" - + "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ"; - for (i = 0; i < b.length; ++i) { - b[i] = (byte) (s.charAt(i) - 'A'); - } - TYPE = b; - - // code to generate the above string - // - // // SBYTE_INSN instructions - // b[Constants.NEWARRAY] = SBYTE_INSN; - // b[Constants.BIPUSH] = SBYTE_INSN; - // - // // SHORT_INSN instructions - // b[Constants.SIPUSH] = SHORT_INSN; - // - // // (IMPL)VAR_INSN instructions - // b[Constants.RET] = VAR_INSN; - // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { - // b[i] = VAR_INSN; - // } - // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { - // b[i] = VAR_INSN; - // } - // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 - // b[i] = IMPLVAR_INSN; - // } - // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 - // b[i] = IMPLVAR_INSN; - // } - // - // // TYPE_INSN instructions - // b[Constants.NEW] = TYPE_INSN; - // b[Constants.ANEWARRAY] = TYPE_INSN; - // b[Constants.CHECKCAST] = TYPE_INSN; - // b[Constants.INSTANCEOF] = TYPE_INSN; - // - // // (Set)FIELDORMETH_INSN instructions - // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { - // b[i] = FIELDORMETH_INSN; - // } - // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; - // b[Constants.INVOKEDYNAMIC] = INDYMETH_INSN; - // - // // LABEL(W)_INSN instructions - // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { - // b[i] = LABEL_INSN; - // } - // b[Constants.IFNULL] = LABEL_INSN; - // b[Constants.IFNONNULL] = LABEL_INSN; - // b[200] = LABELW_INSN; // GOTO_W - // b[201] = LABELW_INSN; // JSR_W - // // temporary opcodes used internally by ASM - see Label and - // MethodWriter - // for (i = 202; i < 220; ++i) { - // b[i] = LABEL_INSN; - // } - // - // // LDC(_W) instructions - // b[Constants.LDC] = LDC_INSN; - // b[19] = LDCW_INSN; // LDC_W - // b[20] = LDCW_INSN; // LDC2_W - // - // // special instructions - // b[Constants.IINC] = IINC_INSN; - // b[Constants.TABLESWITCH] = TABL_INSN; - // b[Constants.LOOKUPSWITCH] = LOOK_INSN; - // b[Constants.MULTIANEWARRAY] = MANA_INSN; - // b[196] = WIDE_INSN; // WIDE - // - // for (i = 0; i < b.length; ++i) { - // System.err.print((char)('A' + b[i])); - // } - // System.err.println(); - } - - // ------------------------------------------------------------------------ - // Constructor - // ------------------------------------------------------------------------ - - /** - * Constructs a new {@link ClassWriter} object. - * - * @param flags - * option flags that can be used to modify the default behavior - * of this class. See {@link #COMPUTE_MAXS}, - * {@link #COMPUTE_FRAMES}. - */ - public ClassWriter(final int flags) { - super(Opcodes.ASM5); - index = 1; - pool = new ByteVector(); - items = new Item[256]; - threshold = (int) (0.75d * items.length); - key = new Item(); - key2 = new Item(); - key3 = new Item(); - key4 = new Item(); - this.computeMaxs = (flags & COMPUTE_MAXS) != 0; - this.computeFrames = (flags & COMPUTE_FRAMES) != 0; - } - - /** - * Constructs a new {@link ClassWriter} object and enables optimizations for - * "mostly add" bytecode transformations. These optimizations are the - * following: - * - *
    - *
  • The constant pool from the original class is copied as is in the new - * class, which saves time. New constant pool entries will be added at the - * end if necessary, but unused constant pool entries won't be - * removed.
  • - *
  • Methods that are not transformed are copied as is in the new class, - * directly from the original class bytecode (i.e. without emitting visit - * events for all the method instructions), which saves a lot of - * time. Untransformed methods are detected by the fact that the - * {@link ClassReader} receives {@link MethodVisitor} objects that come from - * a {@link ClassWriter} (and not from any other {@link ClassVisitor} - * instance).
  • - *
- * - * @param classReader - * the {@link ClassReader} used to read the original class. It - * will be used to copy the entire constant pool from the - * original class and also to copy other fragments of original - * bytecode where applicable. - * @param flags - * option flags that can be used to modify the default behavior - * of this class. These option flags do not affect methods - * that are copied as is in the new class. This means that the - * maximum stack size nor the stack frames will be computed for - * these methods. See {@link #COMPUTE_MAXS}, - * {@link #COMPUTE_FRAMES}. - */ - public ClassWriter(final ClassReader classReader, final int flags) { - this(flags); - classReader.copyPool(this); - this.cr = classReader; - } - - // ------------------------------------------------------------------------ - // Implementation of the ClassVisitor abstract class - // ------------------------------------------------------------------------ - - @Override - public final void visit(final int version, final int access, - final String name, final String signature, final String superName, - final String[] interfaces) { - this.version = version; - this.access = access; - this.name = newClass(name); - thisName = name; - if (ClassReader.SIGNATURES && signature != null) { - this.signature = newUTF8(signature); - } - this.superName = superName == null ? 0 : newClass(superName); - if (interfaces != null && interfaces.length > 0) { - interfaceCount = interfaces.length; - this.interfaces = new int[interfaceCount]; - for (int i = 0; i < interfaceCount; ++i) { - this.interfaces[i] = newClass(interfaces[i]); - } - } - } - - @Override - public final void visitSource(final String file, final String debug) { - if (file != null) { - sourceFile = newUTF8(file); - } - if (debug != null) { - sourceDebug = new ByteVector().encodeUTF8(debug, 0, - Integer.MAX_VALUE); - } - } - - @Override - public final void visitOuterClass(final String owner, final String name, - final String desc) { - enclosingMethodOwner = newClass(owner); - if (name != null && desc != null) { - enclosingMethod = newNameType(name, desc); - } - } - - @Override - public final AnnotationVisitor visitAnnotation(final String desc, - final boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write type, and reserve space for values count - bv.putShort(newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); - if (visible) { - aw.next = anns; - anns = aw; - } else { - aw.next = ianns; - ianns = aw; - } - return aw; - } - - @Override - public final AnnotationVisitor visitTypeAnnotation(int typeRef, - TypePath typePath, final String desc, final boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write target_type and target_info - AnnotationWriter.putTarget(typeRef, typePath, bv); - // write type, and reserve space for values count - bv.putShort(newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, - bv.length - 2); - if (visible) { - aw.next = tanns; - tanns = aw; - } else { - aw.next = itanns; - itanns = aw; - } - return aw; - } - - @Override - public final void visitAttribute(final Attribute attr) { - attr.next = attrs; - attrs = attr; - } - - @Override - public final void visitInnerClass(final String name, - final String outerName, final String innerName, final int access) { - if (innerClasses == null) { - innerClasses = new ByteVector(); - } - // Sec. 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the - // constant_pool table which represents a class or interface C that is - // not a package member must have exactly one corresponding entry in the - // classes array". To avoid duplicates we keep track in the intVal field - // of the Item of each CONSTANT_Class_info entry C whether an inner - // class entry has already been added for C (this field is unused for - // class entries, and changing its value does not change the hashcode - // and equality tests). If so we store the index of this inner class - // entry (plus one) in intVal. This hack allows duplicate detection in - // O(1) time. - Item nameItem = newClassItem(name); - if (nameItem.intVal == 0) { - ++innerClassesCount; - innerClasses.putShort(nameItem.index); - innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); - innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); - innerClasses.putShort(access); - nameItem.intVal = innerClassesCount; - } else { - // Compare the inner classes entry nameItem.intVal - 1 with the - // arguments of this method and throw an exception if there is a - // difference? - } - } - - @Override - public final FieldVisitor visitField(final int access, final String name, - final String desc, final String signature, final Object value) { - return new FieldWriter(this, access, name, desc, signature, value); - } - - @Override - public final MethodVisitor visitMethod(final int access, final String name, - final String desc, final String signature, final String[] exceptions) { - return new MethodWriter(this, access, name, desc, signature, - exceptions, computeMaxs, computeFrames); - } - - @Override - public final void visitEnd() { - } - - // ------------------------------------------------------------------------ - // Other public methods - // ------------------------------------------------------------------------ - - /** - * Returns the bytecode of the class that was build with this class writer. - * - * @return the bytecode of the class that was build with this class writer. - */ - public byte[] toByteArray() { - if (index > 0xFFFF) { - throw new RuntimeException("Class file too large!"); - } - // computes the real size of the bytecode of this class - int size = 24 + 2 * interfaceCount; - int nbFields = 0; - FieldWriter fb = firstField; - while (fb != null) { - ++nbFields; - size += fb.getSize(); - fb = (FieldWriter) fb.fv; - } - int nbMethods = 0; - MethodWriter mb = firstMethod; - while (mb != null) { - ++nbMethods; - size += mb.getSize(); - mb = (MethodWriter) mb.mv; - } - int attributeCount = 0; - if (bootstrapMethods != null) { - // we put it as first attribute in order to improve a bit - // ClassReader.copyBootstrapMethods - ++attributeCount; - size += 8 + bootstrapMethods.length; - newUTF8("BootstrapMethods"); - } - if (ClassReader.SIGNATURES && signature != 0) { - ++attributeCount; - size += 8; - newUTF8("Signature"); - } - if (sourceFile != 0) { - ++attributeCount; - size += 8; - newUTF8("SourceFile"); - } - if (sourceDebug != null) { - ++attributeCount; - size += sourceDebug.length + 6; - newUTF8("SourceDebugExtension"); - } - if (enclosingMethodOwner != 0) { - ++attributeCount; - size += 10; - newUTF8("EnclosingMethod"); - } - if ((access & Opcodes.ACC_DEPRECATED) != 0) { - ++attributeCount; - size += 6; - newUTF8("Deprecated"); - } - if ((access & Opcodes.ACC_SYNTHETIC) != 0) { - if ((version & 0xFFFF) < Opcodes.V1_5 - || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { - ++attributeCount; - size += 6; - newUTF8("Synthetic"); - } - } - if (innerClasses != null) { - ++attributeCount; - size += 8 + innerClasses.length; - newUTF8("InnerClasses"); - } - if (ClassReader.ANNOTATIONS && anns != null) { - ++attributeCount; - size += 8 + anns.getSize(); - newUTF8("RuntimeVisibleAnnotations"); - } - if (ClassReader.ANNOTATIONS && ianns != null) { - ++attributeCount; - size += 8 + ianns.getSize(); - newUTF8("RuntimeInvisibleAnnotations"); - } - if (ClassReader.ANNOTATIONS && tanns != null) { - ++attributeCount; - size += 8 + tanns.getSize(); - newUTF8("RuntimeVisibleTypeAnnotations"); - } - if (ClassReader.ANNOTATIONS && itanns != null) { - ++attributeCount; - size += 8 + itanns.getSize(); - newUTF8("RuntimeInvisibleTypeAnnotations"); - } - if (attrs != null) { - attributeCount += attrs.getCount(); - size += attrs.getSize(this, null, 0, -1, -1); - } - size += pool.length; - // allocates a byte vector of this size, in order to avoid unnecessary - // arraycopy operations in the ByteVector.enlarge() method - ByteVector out = new ByteVector(size); - out.putInt(0xCAFEBABE).putInt(version); - out.putShort(index).putByteArray(pool.data, 0, pool.length); - int mask = Opcodes.ACC_DEPRECATED | ACC_SYNTHETIC_ATTRIBUTE - | ((access & ACC_SYNTHETIC_ATTRIBUTE) / TO_ACC_SYNTHETIC); - out.putShort(access & ~mask).putShort(name).putShort(superName); - out.putShort(interfaceCount); - for (int i = 0; i < interfaceCount; ++i) { - out.putShort(interfaces[i]); - } - out.putShort(nbFields); - fb = firstField; - while (fb != null) { - fb.put(out); - fb = (FieldWriter) fb.fv; - } - out.putShort(nbMethods); - mb = firstMethod; - while (mb != null) { - mb.put(out); - mb = (MethodWriter) mb.mv; - } - out.putShort(attributeCount); - if (bootstrapMethods != null) { - out.putShort(newUTF8("BootstrapMethods")); - out.putInt(bootstrapMethods.length + 2).putShort( - bootstrapMethodsCount); - out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); - } - if (ClassReader.SIGNATURES && signature != 0) { - out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); - } - if (sourceFile != 0) { - out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); - } - if (sourceDebug != null) { - int len = sourceDebug.length; - out.putShort(newUTF8("SourceDebugExtension")).putInt(len); - out.putByteArray(sourceDebug.data, 0, len); - } - if (enclosingMethodOwner != 0) { - out.putShort(newUTF8("EnclosingMethod")).putInt(4); - out.putShort(enclosingMethodOwner).putShort(enclosingMethod); - } - if ((access & Opcodes.ACC_DEPRECATED) != 0) { - out.putShort(newUTF8("Deprecated")).putInt(0); - } - if ((access & Opcodes.ACC_SYNTHETIC) != 0) { - if ((version & 0xFFFF) < Opcodes.V1_5 - || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { - out.putShort(newUTF8("Synthetic")).putInt(0); - } - } - if (innerClasses != null) { - out.putShort(newUTF8("InnerClasses")); - out.putInt(innerClasses.length + 2).putShort(innerClassesCount); - out.putByteArray(innerClasses.data, 0, innerClasses.length); - } - if (ClassReader.ANNOTATIONS && anns != null) { - out.putShort(newUTF8("RuntimeVisibleAnnotations")); - anns.put(out); - } - if (ClassReader.ANNOTATIONS && ianns != null) { - out.putShort(newUTF8("RuntimeInvisibleAnnotations")); - ianns.put(out); - } - if (ClassReader.ANNOTATIONS && tanns != null) { - out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); - tanns.put(out); - } - if (ClassReader.ANNOTATIONS && itanns != null) { - out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); - itanns.put(out); - } - if (attrs != null) { - attrs.put(this, null, 0, -1, -1, out); - } - if (invalidFrames) { - anns = null; - ianns = null; - attrs = null; - innerClassesCount = 0; - innerClasses = null; - bootstrapMethodsCount = 0; - bootstrapMethods = null; - firstField = null; - lastField = null; - firstMethod = null; - lastMethod = null; - computeMaxs = false; - computeFrames = true; - invalidFrames = false; - new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES); - return toByteArray(); - } - return out.data; - } - - // ------------------------------------------------------------------------ - // Utility methods: constant pool management - // ------------------------------------------------------------------------ - - /** - * Adds a number or string constant to the constant pool of the class being - * build. Does nothing if the constant pool already contains a similar item. - * - * @param cst - * the value of the constant to be added to the constant pool. - * This parameter must be an {@link Integer}, a {@link Float}, a - * {@link Long}, a {@link Double}, a {@link String} or a - * {@link Type}. - * @return a new or already existing constant item with the given value. - */ - Item newConstItem(final Object cst) { - if (cst instanceof Integer) { - int val = ((Integer) cst).intValue(); - return newInteger(val); - } else if (cst instanceof Byte) { - int val = ((Byte) cst).intValue(); - return newInteger(val); - } else if (cst instanceof Character) { - int val = ((Character) cst).charValue(); - return newInteger(val); - } else if (cst instanceof Short) { - int val = ((Short) cst).intValue(); - return newInteger(val); - } else if (cst instanceof Boolean) { - int val = ((Boolean) cst).booleanValue() ? 1 : 0; - return newInteger(val); - } else if (cst instanceof Float) { - float val = ((Float) cst).floatValue(); - return newFloat(val); - } else if (cst instanceof Long) { - long val = ((Long) cst).longValue(); - return newLong(val); - } else if (cst instanceof Double) { - double val = ((Double) cst).doubleValue(); - return newDouble(val); - } else if (cst instanceof String) { - return newString((String) cst); - } else if (cst instanceof Type) { - Type t = (Type) cst; - int s = t.getSort(); - if (s == Type.OBJECT) { - return newClassItem(t.getInternalName()); - } else if (s == Type.METHOD) { - return newMethodTypeItem(t.getDescriptor()); - } else { // s == primitive type or array - return newClassItem(t.getDescriptor()); - } - } else if (cst instanceof Handle) { - Handle h = (Handle) cst; - return newHandleItem(h.tag, h.owner, h.name, h.desc); - } else { - throw new IllegalArgumentException("value " + cst); - } - } - - /** - * Adds a number or string constant to the constant pool of the class being - * build. Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param cst - * the value of the constant to be added to the constant pool. - * This parameter must be an {@link Integer}, a {@link Float}, a - * {@link Long}, a {@link Double} or a {@link String}. - * @return the index of a new or already existing constant item with the - * given value. - */ - public int newConst(final Object cst) { - return newConstItem(cst).index; - } - - /** - * Adds an UTF8 string to the constant pool of the class being build. Does - * nothing if the constant pool already contains a similar item. This - * method is intended for {@link Attribute} sub classes, and is normally not - * needed by class generators or adapters. - * - * @param value - * the String value. - * @return the index of a new or already existing UTF8 item. - */ - public int newUTF8(final String value) { - key.set(UTF8, value, null, null); - Item result = get(key); - if (result == null) { - pool.putByte(UTF8).putUTF8(value); - result = new Item(index++, key); - put(result); - } - return result.index; - } - - /** - * Adds a class reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param value - * the internal name of the class. - * @return a new or already existing class reference item. - */ - Item newClassItem(final String value) { - key2.set(CLASS, value, null, null); - Item result = get(key2); - if (result == null) { - pool.put12(CLASS, newUTF8(value)); - result = new Item(index++, key2); - put(result); - } - return result; - } - - /** - * Adds a class reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param value - * the internal name of the class. - * @return the index of a new or already existing class reference item. - */ - public int newClass(final String value) { - return newClassItem(value).index; - } - - /** - * Adds a method type reference to the constant pool of the class being - * build. Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param methodDesc - * method descriptor of the method type. - * @return a new or already existing method type reference item. - */ - Item newMethodTypeItem(final String methodDesc) { - key2.set(MTYPE, methodDesc, null, null); - Item result = get(key2); - if (result == null) { - pool.put12(MTYPE, newUTF8(methodDesc)); - result = new Item(index++, key2); - put(result); - } - return result; - } - - /** - * Adds a method type reference to the constant pool of the class being - * build. Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param methodDesc - * method descriptor of the method type. - * @return the index of a new or already existing method type reference - * item. - */ - public int newMethodType(final String methodDesc) { - return newMethodTypeItem(methodDesc).index; - } - - /** - * Adds a handle to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. This method is - * intended for {@link Attribute} sub classes, and is normally not needed by - * class generators or adapters. - * - * @param tag - * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, - * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, - * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, - * {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or - * {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner - * the internal name of the field or method owner class. - * @param name - * the name of the field or method. - * @param desc - * the descriptor of the field or method. - * @return a new or an already existing method type reference item. - */ - Item newHandleItem(final int tag, final String owner, final String name, - final String desc) { - key4.set(HANDLE_BASE + tag, owner, name, desc); - Item result = get(key4); - if (result == null) { - if (tag <= Opcodes.H_PUTSTATIC) { - put112(HANDLE, tag, newField(owner, name, desc)); - } else { - put112(HANDLE, - tag, - newMethod(owner, name, desc, - tag == Opcodes.H_INVOKEINTERFACE)); - } - result = new Item(index++, key4); - put(result); - } - return result; - } - - /** - * Adds a handle to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. This method is - * intended for {@link Attribute} sub classes, and is normally not needed by - * class generators or adapters. - * - * @param tag - * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, - * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, - * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, - * {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or - * {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner - * the internal name of the field or method owner class. - * @param name - * the name of the field or method. - * @param desc - * the descriptor of the field or method. - * @return the index of a new or already existing method type reference - * item. - */ - public int newHandle(final int tag, final String owner, final String name, - final String desc) { - return newHandleItem(tag, owner, name, desc).index; - } - - /** - * Adds an invokedynamic reference to the constant pool of the class being - * build. Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param name - * name of the invoked method. - * @param desc - * descriptor of the invoke method. - * @param bsm - * the bootstrap method. - * @param bsmArgs - * the bootstrap method constant arguments. - * - * @return a new or an already existing invokedynamic type reference item. - */ - Item newInvokeDynamicItem(final String name, final String desc, - final Handle bsm, final Object... bsmArgs) { - // cache for performance - ByteVector bootstrapMethods = this.bootstrapMethods; - if (bootstrapMethods == null) { - bootstrapMethods = this.bootstrapMethods = new ByteVector(); - } - - int position = bootstrapMethods.length; // record current position - - int hashCode = bsm.hashCode(); - bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name, - bsm.desc)); - - int argsLength = bsmArgs.length; - bootstrapMethods.putShort(argsLength); - - for (int i = 0; i < argsLength; i++) { - Object bsmArg = bsmArgs[i]; - hashCode ^= bsmArg.hashCode(); - bootstrapMethods.putShort(newConst(bsmArg)); - } - - byte[] data = bootstrapMethods.data; - int length = (1 + 1 + argsLength) << 1; // (bsm + argCount + arguments) - hashCode &= 0x7FFFFFFF; - Item result = items[hashCode % items.length]; - loop: while (result != null) { - if (result.type != BSM || result.hashCode != hashCode) { - result = result.next; - continue; - } - - // because the data encode the size of the argument - // we don't need to test if these size are equals - int resultPosition = result.intVal; - for (int p = 0; p < length; p++) { - if (data[position + p] != data[resultPosition + p]) { - result = result.next; - continue loop; - } - } - break; - } - - int bootstrapMethodIndex; - if (result != null) { - bootstrapMethodIndex = result.index; - bootstrapMethods.length = position; // revert to old position - } else { - bootstrapMethodIndex = bootstrapMethodsCount++; - result = new Item(bootstrapMethodIndex); - result.set(position, hashCode); - put(result); - } - - // now, create the InvokeDynamic constant - key3.set(name, desc, bootstrapMethodIndex); - result = get(key3); - if (result == null) { - put122(INDY, bootstrapMethodIndex, newNameType(name, desc)); - result = new Item(index++, key3); - put(result); - } - return result; - } - - /** - * Adds an invokedynamic reference to the constant pool of the class being - * build. Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param name - * name of the invoked method. - * @param desc - * descriptor of the invoke method. - * @param bsm - * the bootstrap method. - * @param bsmArgs - * the bootstrap method constant arguments. - * - * @return the index of a new or already existing invokedynamic reference - * item. - */ - public int newInvokeDynamic(final String name, final String desc, - final Handle bsm, final Object... bsmArgs) { - return newInvokeDynamicItem(name, desc, bsm, bsmArgs).index; - } - - /** - * Adds a field reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * - * @param owner - * the internal name of the field's owner class. - * @param name - * the field's name. - * @param desc - * the field's descriptor. - * @return a new or already existing field reference item. - */ - Item newFieldItem(final String owner, final String name, final String desc) { - key3.set(FIELD, owner, name, desc); - Item result = get(key3); - if (result == null) { - put122(FIELD, newClass(owner), newNameType(name, desc)); - result = new Item(index++, key3); - put(result); - } - return result; - } - - /** - * Adds a field reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param owner - * the internal name of the field's owner class. - * @param name - * the field's name. - * @param desc - * the field's descriptor. - * @return the index of a new or already existing field reference item. - */ - public int newField(final String owner, final String name, final String desc) { - return newFieldItem(owner, name, desc).index; - } - - /** - * Adds a method reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * - * @param owner - * the internal name of the method's owner class. - * @param name - * the method's name. - * @param desc - * the method's descriptor. - * @param itf - * true if owner is an interface. - * @return a new or already existing method reference item. - */ - Item newMethodItem(final String owner, final String name, - final String desc, final boolean itf) { - int type = itf ? IMETH : METH; - key3.set(type, owner, name, desc); - Item result = get(key3); - if (result == null) { - put122(type, newClass(owner), newNameType(name, desc)); - result = new Item(index++, key3); - put(result); - } - return result; - } - - /** - * Adds a method reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param owner - * the internal name of the method's owner class. - * @param name - * the method's name. - * @param desc - * the method's descriptor. - * @param itf - * true if owner is an interface. - * @return the index of a new or already existing method reference item. - */ - public int newMethod(final String owner, final String name, - final String desc, final boolean itf) { - return newMethodItem(owner, name, desc, itf).index; - } - - /** - * Adds an integer to the constant pool of the class being build. Does - * nothing if the constant pool already contains a similar item. - * - * @param value - * the int value. - * @return a new or already existing int item. - */ - Item newInteger(final int value) { - key.set(value); - Item result = get(key); - if (result == null) { - pool.putByte(INT).putInt(value); - result = new Item(index++, key); - put(result); - } - return result; - } - - /** - * Adds a float to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. - * - * @param value - * the float value. - * @return a new or already existing float item. - */ - Item newFloat(final float value) { - key.set(value); - Item result = get(key); - if (result == null) { - pool.putByte(FLOAT).putInt(key.intVal); - result = new Item(index++, key); - put(result); - } - return result; - } - - /** - * Adds a long to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. - * - * @param value - * the long value. - * @return a new or already existing long item. - */ - Item newLong(final long value) { - key.set(value); - Item result = get(key); - if (result == null) { - pool.putByte(LONG).putLong(value); - result = new Item(index, key); - index += 2; - put(result); - } - return result; - } - - /** - * Adds a double to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. - * - * @param value - * the double value. - * @return a new or already existing double item. - */ - Item newDouble(final double value) { - key.set(value); - Item result = get(key); - if (result == null) { - pool.putByte(DOUBLE).putLong(key.longVal); - result = new Item(index, key); - index += 2; - put(result); - } - return result; - } - - /** - * Adds a string to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. - * - * @param value - * the String value. - * @return a new or already existing string item. - */ - private Item newString(final String value) { - key2.set(STR, value, null, null); - Item result = get(key2); - if (result == null) { - pool.put12(STR, newUTF8(value)); - result = new Item(index++, key2); - put(result); - } - return result; - } - - /** - * Adds a name and type to the constant pool of the class being build. Does - * nothing if the constant pool already contains a similar item. This - * method is intended for {@link Attribute} sub classes, and is normally not - * needed by class generators or adapters. - * - * @param name - * a name. - * @param desc - * a type descriptor. - * @return the index of a new or already existing name and type item. - */ - public int newNameType(final String name, final String desc) { - return newNameTypeItem(name, desc).index; - } - - /** - * Adds a name and type to the constant pool of the class being build. Does - * nothing if the constant pool already contains a similar item. - * - * @param name - * a name. - * @param desc - * a type descriptor. - * @return a new or already existing name and type item. - */ - Item newNameTypeItem(final String name, final String desc) { - key2.set(NAME_TYPE, name, desc, null); - Item result = get(key2); - if (result == null) { - put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); - result = new Item(index++, key2); - put(result); - } - return result; - } - - /** - * Adds the given internal name to {@link #typeTable} and returns its index. - * Does nothing if the type table already contains this internal name. - * - * @param type - * the internal name to be added to the type table. - * @return the index of this internal name in the type table. - */ - int addType(final String type) { - key.set(TYPE_NORMAL, type, null, null); - Item result = get(key); - if (result == null) { - result = addType(key); - } - return result.index; - } - - /** - * Adds the given "uninitialized" type to {@link #typeTable} and returns its - * index. This method is used for UNINITIALIZED types, made of an internal - * name and a bytecode offset. - * - * @param type - * the internal name to be added to the type table. - * @param offset - * the bytecode offset of the NEW instruction that created this - * UNINITIALIZED type value. - * @return the index of this internal name in the type table. - */ - int addUninitializedType(final String type, final int offset) { - key.type = TYPE_UNINIT; - key.intVal = offset; - key.strVal1 = type; - key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset); - Item result = get(key); - if (result == null) { - result = addType(key); - } - return result.index; - } - - /** - * Adds the given Item to {@link #typeTable}. - * - * @param item - * the value to be added to the type table. - * @return the added Item, which a new Item instance with the same value as - * the given Item. - */ - private Item addType(final Item item) { - ++typeCount; - Item result = new Item(typeCount, key); - put(result); - if (typeTable == null) { - typeTable = new Item[16]; - } - if (typeCount == typeTable.length) { - Item[] newTable = new Item[2 * typeTable.length]; - System.arraycopy(typeTable, 0, newTable, 0, typeTable.length); - typeTable = newTable; - } - typeTable[typeCount] = result; - return result; - } - - /** - * Returns the index of the common super type of the two given types. This - * method calls {@link #getCommonSuperClass} and caches the result in the - * {@link #items} hash table to speedup future calls with the same - * parameters. - * - * @param type1 - * index of an internal name in {@link #typeTable}. - * @param type2 - * index of an internal name in {@link #typeTable}. - * @return the index of the common super type of the two given types. - */ - int getMergedType(final int type1, final int type2) { - key2.type = TYPE_MERGED; - key2.longVal = type1 | (((long) type2) << 32); - key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2); - Item result = get(key2); - if (result == null) { - String t = typeTable[type1].strVal1; - String u = typeTable[type2].strVal1; - key2.intVal = addType(getCommonSuperClass(t, u)); - result = new Item((short) 0, key2); - put(result); - } - return result.intVal; - } - - /** - * Returns the common super type of the two given types. The default - * implementation of this method loads the two given classes and uses - * the java.lang.Class methods to find the common super class. It can be - * overridden to compute this common super type in other ways, in particular - * without actually loading any class, or to take into account the class - * that is currently being generated by this ClassWriter, which can of - * course not be loaded since it is under construction. - * - * @param type1 - * the internal name of a class. - * @param type2 - * the internal name of another class. - * @return the internal name of the common super class of the two given - * classes. - */ - protected String getCommonSuperClass(final String type1, final String type2) { - Class c, d; - ClassLoader classLoader = getClass().getClassLoader(); - try { - c = Class.forName(type1.replace('/', '.'), false, classLoader); - d = Class.forName(type2.replace('/', '.'), false, classLoader); - } catch (Exception e) { - throw new RuntimeException(e.toString()); - } - if (c.isAssignableFrom(d)) { - return type1; - } - if (d.isAssignableFrom(c)) { - return type2; - } - if (c.isInterface() || d.isInterface()) { - return "java/lang/Object"; - } else { - do { - c = c.getSuperclass(); - } while (!c.isAssignableFrom(d)); - return c.getName().replace('.', '/'); - } - } - - /** - * Returns the constant pool's hash table item which is equal to the given - * item. - * - * @param key - * a constant pool item. - * @return the constant pool's hash table item which is equal to the given - * item, or null if there is no such item. - */ - private Item get(final Item key) { - Item i = items[key.hashCode % items.length]; - while (i != null && (i.type != key.type || !key.isEqualTo(i))) { - i = i.next; - } - return i; - } - - /** - * Puts the given item in the constant pool's hash table. The hash table - * must not already contains this item. - * - * @param i - * the item to be added to the constant pool's hash table. - */ - private void put(final Item i) { - if (index + typeCount > threshold) { - int ll = items.length; - int nl = ll * 2 + 1; - Item[] newItems = new Item[nl]; - for (int l = ll - 1; l >= 0; --l) { - Item j = items[l]; - while (j != null) { - int index = j.hashCode % newItems.length; - Item k = j.next; - j.next = newItems[index]; - newItems[index] = j; - j = k; - } - } - items = newItems; - threshold = (int) (nl * 0.75); - } - int index = i.hashCode % items.length; - i.next = items[index]; - items[index] = i; - } - - /** - * Puts one byte and two shorts into the constant pool. - * - * @param b - * a byte. - * @param s1 - * a short. - * @param s2 - * another short. - */ - private void put122(final int b, final int s1, final int s2) { - pool.put12(b, s1).putShort(s2); - } - - /** - * Puts two bytes and one short into the constant pool. - * - * @param b1 - * a byte. - * @param b2 - * another byte. - * @param s - * a short. - */ - private void put112(final int b1, final int b2, final int s) { - pool.put11(b1, b2).putShort(s); - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Context.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Context.java deleted file mode 100644 index 13d51d72..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Context.java +++ /dev/null @@ -1,145 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * Information about a class being parsed in a {@link ClassReader}. - * - * @author Eric Bruneton - */ -class Context { - - /** - * Prototypes of the attributes that must be parsed for this class. - */ - Attribute[] attrs; - - /** - * The {@link ClassReader} option flags for the parsing of this class. - */ - int flags; - - /** - * The buffer used to read strings. - */ - char[] buffer; - - /** - * The start index of each bootstrap method. - */ - int[] bootstrapMethods; - - /** - * The access flags of the method currently being parsed. - */ - int access; - - /** - * The name of the method currently being parsed. - */ - String name; - - /** - * The descriptor of the method currently being parsed. - */ - String desc; - - /** - * The label objects, indexed by bytecode offset, of the method currently - * being parsed (only bytecode offsets for which a label is needed have a - * non null associated Label object). - */ - Label[] labels; - - /** - * The target of the type annotation currently being parsed. - */ - int typeRef; - - /** - * The path of the type annotation currently being parsed. - */ - TypePath typePath; - - /** - * The offset of the latest stack map frame that has been parsed. - */ - int offset; - - /** - * The labels corresponding to the start of the local variable ranges in the - * local variable type annotation currently being parsed. - */ - Label[] start; - - /** - * The labels corresponding to the end of the local variable ranges in the - * local variable type annotation currently being parsed. - */ - Label[] end; - - /** - * The local variable indices for each local variable range in the local - * variable type annotation currently being parsed. - */ - int[] index; - - /** - * The encoding of the latest stack map frame that has been parsed. - */ - int mode; - - /** - * The number of locals in the latest stack map frame that has been parsed. - */ - int localCount; - - /** - * The number locals in the latest stack map frame that has been parsed, - * minus the number of locals in the previous frame. - */ - int localDiff; - - /** - * The local values of the latest stack map frame that has been parsed. - */ - Object[] local; - - /** - * The stack size of the latest stack map frame that has been parsed. - */ - int stackCount; - - /** - * The stack values of the latest stack map frame that has been parsed. - */ - Object[] stack; -} \ No newline at end of file diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Edge.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Edge.java deleted file mode 100644 index 592d5ccc..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Edge.java +++ /dev/null @@ -1,75 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * An edge in the control flow graph of a method body. See {@link Label Label}. - * - * @author Eric Bruneton - */ -class Edge { - - /** - * Denotes a normal control flow graph edge. - */ - static final int NORMAL = 0; - - /** - * Denotes a control flow graph edge corresponding to an exception handler. - * More precisely any {@link Edge} whose {@link #info} is strictly positive - * corresponds to an exception handler. The actual value of {@link #info} is - * the index, in the {@link ClassWriter} type table, of the exception that - * is catched. - */ - static final int EXCEPTION = 0x7FFFFFFF; - - /** - * Information about this control flow graph edge. If - * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative) - * stack size in the basic block from which this edge originates. This size - * is equal to the stack size at the "jump" instruction to which this edge - * corresponds, relatively to the stack size at the beginning of the - * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used, - * this field is the kind of this control flow graph edge (i.e. NORMAL or - * EXCEPTION). - */ - int info; - - /** - * The successor block of the basic block from which this edge originates. - */ - Label successor; - - /** - * The next edge in the list of successors of the originating basic block. - * See {@link Label#successors successors}. - */ - Edge next; -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/FieldVisitor.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/FieldVisitor.java deleted file mode 100644 index 24dfd379..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/FieldVisitor.java +++ /dev/null @@ -1,150 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A visitor to visit a Java field. The methods of this class must be called in - * the following order: ( visitAnnotation | - * visitTypeAnnotation | visitAttribute )* visitEnd. - * - * @author Eric Bruneton - */ -public abstract class FieldVisitor { - - /** - * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - */ - protected final int api; - - /** - * The field visitor to which this visitor must delegate method calls. May - * be null. - */ - protected FieldVisitor fv; - - /** - * Constructs a new {@link FieldVisitor}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - */ - public FieldVisitor(final int api) { - this(api, null); - } - - /** - * Constructs a new {@link FieldVisitor}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - * @param fv - * the field visitor to which this visitor must delegate method - * calls. May be null. - */ - public FieldVisitor(final int api, final FieldVisitor fv) { - if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { - throw new IllegalArgumentException(); - } - this.api = api; - this.fv = fv; - } - - /** - * Visits an annotation of the field. - * - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (fv != null) { - return fv.visitAnnotation(desc, visible); - } - return null; - } - - /** - * Visits an annotation on the type of the field. - * - * @param typeRef - * a reference to the annotated type. The sort of this type - * reference must be {@link TypeReference#FIELD FIELD}. See - * {@link TypeReference}. - * @param typePath - * the path to the annotated type argument, wildcard bound, array - * element type, or static inner type within 'typeRef'. May be - * null if the annotation targets 'typeRef' as a whole. - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitTypeAnnotation(int typeRef, - TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } - if (fv != null) { - return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); - } - return null; - } - - /** - * Visits a non standard attribute of the field. - * - * @param attr - * an attribute. - */ - public void visitAttribute(Attribute attr) { - if (fv != null) { - fv.visitAttribute(attr); - } - } - - /** - * Visits the end of the field. This method, which is the last one to be - * called, is used to inform the visitor that all the annotations and - * attributes of the field have been visited. - */ - public void visitEnd() { - if (fv != null) { - fv.visitEnd(); - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/FieldWriter.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/FieldWriter.java deleted file mode 100644 index 832bea02..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/FieldWriter.java +++ /dev/null @@ -1,329 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * An {@link FieldVisitor} that generates Java fields in bytecode form. - * - * @author Eric Bruneton - */ -final class FieldWriter extends FieldVisitor { - - /** - * The class writer to which this field must be added. - */ - private final ClassWriter cw; - - /** - * Access flags of this field. - */ - private final int access; - - /** - * The index of the constant pool item that contains the name of this - * method. - */ - private final int name; - - /** - * The index of the constant pool item that contains the descriptor of this - * field. - */ - private final int desc; - - /** - * The index of the constant pool item that contains the signature of this - * field. - */ - private int signature; - - /** - * The index of the constant pool item that contains the constant value of - * this field. - */ - private int value; - - /** - * The runtime visible annotations of this field. May be null. - */ - private AnnotationWriter anns; - - /** - * The runtime invisible annotations of this field. May be null. - */ - private AnnotationWriter ianns; - - /** - * The runtime visible type annotations of this field. May be null. - */ - private AnnotationWriter tanns; - - /** - * The runtime invisible type annotations of this field. May be - * null. - */ - private AnnotationWriter itanns; - - /** - * The non standard attributes of this field. May be null. - */ - private Attribute attrs; - - // ------------------------------------------------------------------------ - // Constructor - // ------------------------------------------------------------------------ - - /** - * Constructs a new {@link FieldWriter}. - * - * @param cw - * the class writer to which this field must be added. - * @param access - * the field's access flags (see {@link Opcodes}). - * @param name - * the field's name. - * @param desc - * the field's descriptor (see {@link Type}). - * @param signature - * the field's signature. May be null. - * @param value - * the field's constant value. May be null. - */ - FieldWriter(final ClassWriter cw, final int access, final String name, - final String desc, final String signature, final Object value) { - super(Opcodes.ASM5); - if (cw.firstField == null) { - cw.firstField = this; - } else { - cw.lastField.fv = this; - } - cw.lastField = this; - this.cw = cw; - this.access = access; - this.name = cw.newUTF8(name); - this.desc = cw.newUTF8(desc); - if (ClassReader.SIGNATURES && signature != null) { - this.signature = cw.newUTF8(signature); - } - if (value != null) { - this.value = cw.newConstItem(value).index; - } - } - - // ------------------------------------------------------------------------ - // Implementation of the FieldVisitor abstract class - // ------------------------------------------------------------------------ - - @Override - public AnnotationVisitor visitAnnotation(final String desc, - final boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); - if (visible) { - aw.next = anns; - anns = aw; - } else { - aw.next = ianns; - ianns = aw; - } - return aw; - } - - @Override - public AnnotationVisitor visitTypeAnnotation(final int typeRef, - final TypePath typePath, final String desc, final boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write target_type and target_info - AnnotationWriter.putTarget(typeRef, typePath, bv); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, - bv.length - 2); - if (visible) { - aw.next = tanns; - tanns = aw; - } else { - aw.next = itanns; - itanns = aw; - } - return aw; - } - - @Override - public void visitAttribute(final Attribute attr) { - attr.next = attrs; - attrs = attr; - } - - @Override - public void visitEnd() { - } - - // ------------------------------------------------------------------------ - // Utility methods - // ------------------------------------------------------------------------ - - /** - * Returns the size of this field. - * - * @return the size of this field. - */ - int getSize() { - int size = 8; - if (value != 0) { - cw.newUTF8("ConstantValue"); - size += 8; - } - if ((access & Opcodes.ACC_SYNTHETIC) != 0) { - if ((cw.version & 0xFFFF) < Opcodes.V1_5 - || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { - cw.newUTF8("Synthetic"); - size += 6; - } - } - if ((access & Opcodes.ACC_DEPRECATED) != 0) { - cw.newUTF8("Deprecated"); - size += 6; - } - if (ClassReader.SIGNATURES && signature != 0) { - cw.newUTF8("Signature"); - size += 8; - } - if (ClassReader.ANNOTATIONS && anns != null) { - cw.newUTF8("RuntimeVisibleAnnotations"); - size += 8 + anns.getSize(); - } - if (ClassReader.ANNOTATIONS && ianns != null) { - cw.newUTF8("RuntimeInvisibleAnnotations"); - size += 8 + ianns.getSize(); - } - if (ClassReader.ANNOTATIONS && tanns != null) { - cw.newUTF8("RuntimeVisibleTypeAnnotations"); - size += 8 + tanns.getSize(); - } - if (ClassReader.ANNOTATIONS && itanns != null) { - cw.newUTF8("RuntimeInvisibleTypeAnnotations"); - size += 8 + itanns.getSize(); - } - if (attrs != null) { - size += attrs.getSize(cw, null, 0, -1, -1); - } - return size; - } - - /** - * Puts the content of this field into the given byte vector. - * - * @param out - * where the content of this field must be put. - */ - void put(final ByteVector out) { - final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; - int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE - | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); - out.putShort(access & ~mask).putShort(name).putShort(desc); - int attributeCount = 0; - if (value != 0) { - ++attributeCount; - } - if ((access & Opcodes.ACC_SYNTHETIC) != 0) { - if ((cw.version & 0xFFFF) < Opcodes.V1_5 - || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { - ++attributeCount; - } - } - if ((access & Opcodes.ACC_DEPRECATED) != 0) { - ++attributeCount; - } - if (ClassReader.SIGNATURES && signature != 0) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && anns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && ianns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && tanns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && itanns != null) { - ++attributeCount; - } - if (attrs != null) { - attributeCount += attrs.getCount(); - } - out.putShort(attributeCount); - if (value != 0) { - out.putShort(cw.newUTF8("ConstantValue")); - out.putInt(2).putShort(value); - } - if ((access & Opcodes.ACC_SYNTHETIC) != 0) { - if ((cw.version & 0xFFFF) < Opcodes.V1_5 - || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { - out.putShort(cw.newUTF8("Synthetic")).putInt(0); - } - } - if ((access & Opcodes.ACC_DEPRECATED) != 0) { - out.putShort(cw.newUTF8("Deprecated")).putInt(0); - } - if (ClassReader.SIGNATURES && signature != 0) { - out.putShort(cw.newUTF8("Signature")); - out.putInt(2).putShort(signature); - } - if (ClassReader.ANNOTATIONS && anns != null) { - out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); - anns.put(out); - } - if (ClassReader.ANNOTATIONS && ianns != null) { - out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); - ianns.put(out); - } - if (ClassReader.ANNOTATIONS && tanns != null) { - out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); - tanns.put(out); - } - if (ClassReader.ANNOTATIONS && itanns != null) { - out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); - itanns.put(out); - } - if (attrs != null) { - attrs.put(cw, null, 0, -1, -1, out); - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Frame.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Frame.java deleted file mode 100644 index 07e4db63..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Frame.java +++ /dev/null @@ -1,1462 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * Information about the input and output stack map frames of a basic block. - * - * @author Eric Bruneton - */ -final class Frame { - - /* - * Frames are computed in a two steps process: during the visit of each - * instruction, the state of the frame at the end of current basic block is - * updated by simulating the action of the instruction on the previous state - * of this so called "output frame". In visitMaxs, a fix point algorithm is - * used to compute the "input frame" of each basic block, i.e. the stack map - * frame at the beginning of the basic block, starting from the input frame - * of the first basic block (which is computed from the method descriptor), - * and by using the previously computed output frames to compute the input - * state of the other blocks. - * - * All output and input frames are stored as arrays of integers. Reference - * and array types are represented by an index into a type table (which is - * not the same as the constant pool of the class, in order to avoid adding - * unnecessary constants in the pool - not all computed frames will end up - * being stored in the stack map table). This allows very fast type - * comparisons. - * - * Output stack map frames are computed relatively to the input frame of the - * basic block, which is not yet known when output frames are computed. It - * is therefore necessary to be able to represent abstract types such as - * "the type at position x in the input frame locals" or "the type at - * position x from the top of the input frame stack" or even "the type at - * position x in the input frame, with y more (or less) array dimensions". - * This explains the rather complicated type format used in output frames. - * - * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a - * signed number of array dimensions (from -8 to 7). KIND is either BASE, - * LOCAL or STACK. BASE is used for types that are not relative to the input - * frame. LOCAL is used for types that are relative to the input local - * variable types. STACK is used for types that are relative to the input - * stack types. VALUE depends on KIND. For LOCAL types, it is an index in - * the input local variable types. For STACK types, it is a position - * relatively to the top of input frame stack. For BASE types, it is either - * one of the constants defined below, or for OBJECT and UNINITIALIZED - * types, a tag and an index in the type table. - * - * Output frames can contain types of any kind and with a positive or - * negative dimension (and even unassigned types, represented by 0 - which - * does not correspond to any valid type value). Input frames can only - * contain BASE types of positive or null dimension. In all cases the type - * table contains only internal type names (array type descriptors are - * forbidden - dimensions must be represented through the DIM field). - * - * The LONG and DOUBLE types are always represented by using two slots (LONG - * + TOP or DOUBLE + TOP), for local variable types as well as in the - * operand stack. This is necessary to be able to simulate DUPx_y - * instructions, whose effect would be dependent on the actual type values - * if types were always represented by a single slot in the stack (and this - * is not possible, since actual type values are not always known - cf LOCAL - * and STACK type kinds). - */ - - /** - * Mask to get the dimension of a frame type. This dimension is a signed - * integer between -8 and 7. - */ - static final int DIM = 0xF0000000; - - /** - * Constant to be added to a type to get a type with one more dimension. - */ - static final int ARRAY_OF = 0x10000000; - - /** - * Constant to be added to a type to get a type with one less dimension. - */ - static final int ELEMENT_OF = 0xF0000000; - - /** - * Mask to get the kind of a frame type. - * - * @see #BASE - * @see #LOCAL - * @see #STACK - */ - static final int KIND = 0xF000000; - - /** - * Flag used for LOCAL and STACK types. Indicates that if this type happens - * to be a long or double type (during the computations of input frames), - * then it must be set to TOP because the second word of this value has been - * reused to store other data in the basic block. Hence the first word no - * longer stores a valid long or double value. - */ - static final int TOP_IF_LONG_OR_DOUBLE = 0x800000; - - /** - * Mask to get the value of a frame type. - */ - static final int VALUE = 0x7FFFFF; - - /** - * Mask to get the kind of base types. - */ - static final int BASE_KIND = 0xFF00000; - - /** - * Mask to get the value of base types. - */ - static final int BASE_VALUE = 0xFFFFF; - - /** - * Kind of the types that are not relative to an input stack map frame. - */ - static final int BASE = 0x1000000; - - /** - * Base kind of the base reference types. The BASE_VALUE of such types is an - * index into the type table. - */ - static final int OBJECT = BASE | 0x700000; - - /** - * Base kind of the uninitialized base types. The BASE_VALUE of such types - * in an index into the type table (the Item at that index contains both an - * instruction offset and an internal class name). - */ - static final int UNINITIALIZED = BASE | 0x800000; - - /** - * Kind of the types that are relative to the local variable types of an - * input stack map frame. The value of such types is a local variable index. - */ - private static final int LOCAL = 0x2000000; - - /** - * Kind of the the types that are relative to the stack of an input stack - * map frame. The value of such types is a position relatively to the top of - * this stack. - */ - private static final int STACK = 0x3000000; - - /** - * The TOP type. This is a BASE type. - */ - static final int TOP = BASE | 0; - - /** - * The BOOLEAN type. This is a BASE type mainly used for array types. - */ - static final int BOOLEAN = BASE | 9; - - /** - * The BYTE type. This is a BASE type mainly used for array types. - */ - static final int BYTE = BASE | 10; - - /** - * The CHAR type. This is a BASE type mainly used for array types. - */ - static final int CHAR = BASE | 11; - - /** - * The SHORT type. This is a BASE type mainly used for array types. - */ - static final int SHORT = BASE | 12; - - /** - * The INTEGER type. This is a BASE type. - */ - static final int INTEGER = BASE | 1; - - /** - * The FLOAT type. This is a BASE type. - */ - static final int FLOAT = BASE | 2; - - /** - * The DOUBLE type. This is a BASE type. - */ - static final int DOUBLE = BASE | 3; - - /** - * The LONG type. This is a BASE type. - */ - static final int LONG = BASE | 4; - - /** - * The NULL type. This is a BASE type. - */ - static final int NULL = BASE | 5; - - /** - * The UNINITIALIZED_THIS type. This is a BASE type. - */ - static final int UNINITIALIZED_THIS = BASE | 6; - - /** - * The stack size variation corresponding to each JVM instruction. This - * stack variation is equal to the size of the values produced by an - * instruction, minus the size of the values consumed by this instruction. - */ - static final int[] SIZE; - - /** - * Computes the stack size variation corresponding to each JVM instruction. - */ - static { - int i; - int[] b = new int[202]; - String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" - + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" - + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" - + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; - for (i = 0; i < b.length; ++i) { - b[i] = s.charAt(i) - 'E'; - } - SIZE = b; - - // code to generate the above string - // - // int NA = 0; // not applicable (unused opcode or variable size opcode) - // - // b = new int[] { - // 0, //NOP, // visitInsn - // 1, //ACONST_NULL, // - - // 1, //ICONST_M1, // - - // 1, //ICONST_0, // - - // 1, //ICONST_1, // - - // 1, //ICONST_2, // - - // 1, //ICONST_3, // - - // 1, //ICONST_4, // - - // 1, //ICONST_5, // - - // 2, //LCONST_0, // - - // 2, //LCONST_1, // - - // 1, //FCONST_0, // - - // 1, //FCONST_1, // - - // 1, //FCONST_2, // - - // 2, //DCONST_0, // - - // 2, //DCONST_1, // - - // 1, //BIPUSH, // visitIntInsn - // 1, //SIPUSH, // - - // 1, //LDC, // visitLdcInsn - // NA, //LDC_W, // - - // NA, //LDC2_W, // - - // 1, //ILOAD, // visitVarInsn - // 2, //LLOAD, // - - // 1, //FLOAD, // - - // 2, //DLOAD, // - - // 1, //ALOAD, // - - // NA, //ILOAD_0, // - - // NA, //ILOAD_1, // - - // NA, //ILOAD_2, // - - // NA, //ILOAD_3, // - - // NA, //LLOAD_0, // - - // NA, //LLOAD_1, // - - // NA, //LLOAD_2, // - - // NA, //LLOAD_3, // - - // NA, //FLOAD_0, // - - // NA, //FLOAD_1, // - - // NA, //FLOAD_2, // - - // NA, //FLOAD_3, // - - // NA, //DLOAD_0, // - - // NA, //DLOAD_1, // - - // NA, //DLOAD_2, // - - // NA, //DLOAD_3, // - - // NA, //ALOAD_0, // - - // NA, //ALOAD_1, // - - // NA, //ALOAD_2, // - - // NA, //ALOAD_3, // - - // -1, //IALOAD, // visitInsn - // 0, //LALOAD, // - - // -1, //FALOAD, // - - // 0, //DALOAD, // - - // -1, //AALOAD, // - - // -1, //BALOAD, // - - // -1, //CALOAD, // - - // -1, //SALOAD, // - - // -1, //ISTORE, // visitVarInsn - // -2, //LSTORE, // - - // -1, //FSTORE, // - - // -2, //DSTORE, // - - // -1, //ASTORE, // - - // NA, //ISTORE_0, // - - // NA, //ISTORE_1, // - - // NA, //ISTORE_2, // - - // NA, //ISTORE_3, // - - // NA, //LSTORE_0, // - - // NA, //LSTORE_1, // - - // NA, //LSTORE_2, // - - // NA, //LSTORE_3, // - - // NA, //FSTORE_0, // - - // NA, //FSTORE_1, // - - // NA, //FSTORE_2, // - - // NA, //FSTORE_3, // - - // NA, //DSTORE_0, // - - // NA, //DSTORE_1, // - - // NA, //DSTORE_2, // - - // NA, //DSTORE_3, // - - // NA, //ASTORE_0, // - - // NA, //ASTORE_1, // - - // NA, //ASTORE_2, // - - // NA, //ASTORE_3, // - - // -3, //IASTORE, // visitInsn - // -4, //LASTORE, // - - // -3, //FASTORE, // - - // -4, //DASTORE, // - - // -3, //AASTORE, // - - // -3, //BASTORE, // - - // -3, //CASTORE, // - - // -3, //SASTORE, // - - // -1, //POP, // - - // -2, //POP2, // - - // 1, //DUP, // - - // 1, //DUP_X1, // - - // 1, //DUP_X2, // - - // 2, //DUP2, // - - // 2, //DUP2_X1, // - - // 2, //DUP2_X2, // - - // 0, //SWAP, // - - // -1, //IADD, // - - // -2, //LADD, // - - // -1, //FADD, // - - // -2, //DADD, // - - // -1, //ISUB, // - - // -2, //LSUB, // - - // -1, //FSUB, // - - // -2, //DSUB, // - - // -1, //IMUL, // - - // -2, //LMUL, // - - // -1, //FMUL, // - - // -2, //DMUL, // - - // -1, //IDIV, // - - // -2, //LDIV, // - - // -1, //FDIV, // - - // -2, //DDIV, // - - // -1, //IREM, // - - // -2, //LREM, // - - // -1, //FREM, // - - // -2, //DREM, // - - // 0, //INEG, // - - // 0, //LNEG, // - - // 0, //FNEG, // - - // 0, //DNEG, // - - // -1, //ISHL, // - - // -1, //LSHL, // - - // -1, //ISHR, // - - // -1, //LSHR, // - - // -1, //IUSHR, // - - // -1, //LUSHR, // - - // -1, //IAND, // - - // -2, //LAND, // - - // -1, //IOR, // - - // -2, //LOR, // - - // -1, //IXOR, // - - // -2, //LXOR, // - - // 0, //IINC, // visitIincInsn - // 1, //I2L, // visitInsn - // 0, //I2F, // - - // 1, //I2D, // - - // -1, //L2I, // - - // -1, //L2F, // - - // 0, //L2D, // - - // 0, //F2I, // - - // 1, //F2L, // - - // 1, //F2D, // - - // -1, //D2I, // - - // 0, //D2L, // - - // -1, //D2F, // - - // 0, //I2B, // - - // 0, //I2C, // - - // 0, //I2S, // - - // -3, //LCMP, // - - // -1, //FCMPL, // - - // -1, //FCMPG, // - - // -3, //DCMPL, // - - // -3, //DCMPG, // - - // -1, //IFEQ, // visitJumpInsn - // -1, //IFNE, // - - // -1, //IFLT, // - - // -1, //IFGE, // - - // -1, //IFGT, // - - // -1, //IFLE, // - - // -2, //IF_ICMPEQ, // - - // -2, //IF_ICMPNE, // - - // -2, //IF_ICMPLT, // - - // -2, //IF_ICMPGE, // - - // -2, //IF_ICMPGT, // - - // -2, //IF_ICMPLE, // - - // -2, //IF_ACMPEQ, // - - // -2, //IF_ACMPNE, // - - // 0, //GOTO, // - - // 1, //JSR, // - - // 0, //RET, // visitVarInsn - // -1, //TABLESWITCH, // visiTableSwitchInsn - // -1, //LOOKUPSWITCH, // visitLookupSwitch - // -1, //IRETURN, // visitInsn - // -2, //LRETURN, // - - // -1, //FRETURN, // - - // -2, //DRETURN, // - - // -1, //ARETURN, // - - // 0, //RETURN, // - - // NA, //GETSTATIC, // visitFieldInsn - // NA, //PUTSTATIC, // - - // NA, //GETFIELD, // - - // NA, //PUTFIELD, // - - // NA, //INVOKEVIRTUAL, // visitMethodInsn - // NA, //INVOKESPECIAL, // - - // NA, //INVOKESTATIC, // - - // NA, //INVOKEINTERFACE, // - - // NA, //INVOKEDYNAMIC, // visitInvokeDynamicInsn - // 1, //NEW, // visitTypeInsn - // 0, //NEWARRAY, // visitIntInsn - // 0, //ANEWARRAY, // visitTypeInsn - // 0, //ARRAYLENGTH, // visitInsn - // NA, //ATHROW, // - - // 0, //CHECKCAST, // visitTypeInsn - // 0, //INSTANCEOF, // - - // -1, //MONITORENTER, // visitInsn - // -1, //MONITOREXIT, // - - // NA, //WIDE, // NOT VISITED - // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn - // -1, //IFNULL, // visitJumpInsn - // -1, //IFNONNULL, // - - // NA, //GOTO_W, // - - // NA, //JSR_W, // - - // }; - // for (i = 0; i < b.length; ++i) { - // System.err.print((char)('E' + b[i])); - // } - // System.err.println(); - } - - /** - * The label (i.e. basic block) to which these input and output stack map - * frames correspond. - */ - Label owner; - - /** - * The input stack map frame locals. - */ - int[] inputLocals; - - /** - * The input stack map frame stack. - */ - int[] inputStack; - - /** - * The output stack map frame locals. - */ - private int[] outputLocals; - - /** - * The output stack map frame stack. - */ - private int[] outputStack; - - /** - * Relative size of the output stack. The exact semantics of this field - * depends on the algorithm that is used. - * - * When only the maximum stack size is computed, this field is the size of - * the output stack relatively to the top of the input stack. - * - * When the stack map frames are completely computed, this field is the - * actual number of types in {@link #outputStack}. - */ - private int outputStackTop; - - /** - * Number of types that are initialized in the basic block. - * - * @see #initializations - */ - private int initializationCount; - - /** - * The types that are initialized in the basic block. A constructor - * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace - * every occurence of this type in the local variables and in the - * operand stack. This cannot be done during the first phase of the - * algorithm since, during this phase, the local variables and the operand - * stack are not completely computed. It is therefore necessary to store the - * types on which constructors are invoked in the basic block, in order to - * do this replacement during the second phase of the algorithm, where the - * frames are fully computed. Note that this array can contain types that - * are relative to input locals or to the input stack (see below for the - * description of the algorithm). - */ - private int[] initializations; - - /** - * Returns the output frame local variable type at the given index. - * - * @param local - * the index of the local that must be returned. - * @return the output frame local variable type at the given index. - */ - private int get(final int local) { - if (outputLocals == null || local >= outputLocals.length) { - // this local has never been assigned in this basic block, - // so it is still equal to its value in the input frame - return LOCAL | local; - } else { - int type = outputLocals[local]; - if (type == 0) { - // this local has never been assigned in this basic block, - // so it is still equal to its value in the input frame - type = outputLocals[local] = LOCAL | local; - } - return type; - } - } - - /** - * Sets the output frame local variable type at the given index. - * - * @param local - * the index of the local that must be set. - * @param type - * the value of the local that must be set. - */ - private void set(final int local, final int type) { - // creates and/or resizes the output local variables array if necessary - if (outputLocals == null) { - outputLocals = new int[10]; - } - int n = outputLocals.length; - if (local >= n) { - int[] t = new int[Math.max(local + 1, 2 * n)]; - System.arraycopy(outputLocals, 0, t, 0, n); - outputLocals = t; - } - // sets the local variable - outputLocals[local] = type; - } - - /** - * Pushes a new type onto the output frame stack. - * - * @param type - * the type that must be pushed. - */ - private void push(final int type) { - // creates and/or resizes the output stack array if necessary - if (outputStack == null) { - outputStack = new int[10]; - } - int n = outputStack.length; - if (outputStackTop >= n) { - int[] t = new int[Math.max(outputStackTop + 1, 2 * n)]; - System.arraycopy(outputStack, 0, t, 0, n); - outputStack = t; - } - // pushes the type on the output stack - outputStack[outputStackTop++] = type; - // updates the maximun height reached by the output stack, if needed - int top = owner.inputStackTop + outputStackTop; - if (top > owner.outputStackMax) { - owner.outputStackMax = top; - } - } - - /** - * Pushes a new type onto the output frame stack. - * - * @param cw - * the ClassWriter to which this label belongs. - * @param desc - * the descriptor of the type to be pushed. Can also be a method - * descriptor (in this case this method pushes its return type - * onto the output frame stack). - */ - private void push(final ClassWriter cw, final String desc) { - int type = type(cw, desc); - if (type != 0) { - push(type); - if (type == LONG || type == DOUBLE) { - push(TOP); - } - } - } - - /** - * Returns the int encoding of the given type. - * - * @param cw - * the ClassWriter to which this label belongs. - * @param desc - * a type descriptor. - * @return the int encoding of the given type. - */ - private static int type(final ClassWriter cw, final String desc) { - String t; - int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; - switch (desc.charAt(index)) { - case 'V': - return 0; - case 'Z': - case 'C': - case 'B': - case 'S': - case 'I': - return INTEGER; - case 'F': - return FLOAT; - case 'J': - return LONG; - case 'D': - return DOUBLE; - case 'L': - // stores the internal name, not the descriptor! - t = desc.substring(index + 1, desc.length() - 1); - return OBJECT | cw.addType(t); - // case '[': - default: - // extracts the dimensions and the element type - int data; - int dims = index + 1; - while (desc.charAt(dims) == '[') { - ++dims; - } - switch (desc.charAt(dims)) { - case 'Z': - data = BOOLEAN; - break; - case 'C': - data = CHAR; - break; - case 'B': - data = BYTE; - break; - case 'S': - data = SHORT; - break; - case 'I': - data = INTEGER; - break; - case 'F': - data = FLOAT; - break; - case 'J': - data = LONG; - break; - case 'D': - data = DOUBLE; - break; - // case 'L': - default: - // stores the internal name, not the descriptor - t = desc.substring(dims + 1, desc.length() - 1); - data = OBJECT | cw.addType(t); - } - return (dims - index) << 28 | data; - } - } - - /** - * Pops a type from the output frame stack and returns its value. - * - * @return the type that has been popped from the output frame stack. - */ - private int pop() { - if (outputStackTop > 0) { - return outputStack[--outputStackTop]; - } else { - // if the output frame stack is empty, pops from the input stack - return STACK | -(--owner.inputStackTop); - } - } - - /** - * Pops the given number of types from the output frame stack. - * - * @param elements - * the number of types that must be popped. - */ - private void pop(final int elements) { - if (outputStackTop >= elements) { - outputStackTop -= elements; - } else { - // if the number of elements to be popped is greater than the number - // of elements in the output stack, clear it, and pops the remaining - // elements from the input stack. - owner.inputStackTop -= elements - outputStackTop; - outputStackTop = 0; - } - } - - /** - * Pops a type from the output frame stack. - * - * @param desc - * the descriptor of the type to be popped. Can also be a method - * descriptor (in this case this method pops the types - * corresponding to the method arguments). - */ - private void pop(final String desc) { - char c = desc.charAt(0); - if (c == '(') { - pop((Type.getArgumentsAndReturnSizes(desc) >> 2) - 1); - } else if (c == 'J' || c == 'D') { - pop(2); - } else { - pop(1); - } - } - - /** - * Adds a new type to the list of types on which a constructor is invoked in - * the basic block. - * - * @param var - * a type on a which a constructor is invoked. - */ - private void init(final int var) { - // creates and/or resizes the initializations array if necessary - if (initializations == null) { - initializations = new int[2]; - } - int n = initializations.length; - if (initializationCount >= n) { - int[] t = new int[Math.max(initializationCount + 1, 2 * n)]; - System.arraycopy(initializations, 0, t, 0, n); - initializations = t; - } - // stores the type to be initialized - initializations[initializationCount++] = var; - } - - /** - * Replaces the given type with the appropriate type if it is one of the - * types on which a constructor is invoked in the basic block. - * - * @param cw - * the ClassWriter to which this label belongs. - * @param t - * a type - * @return t or, if t is one of the types on which a constructor is invoked - * in the basic block, the type corresponding to this constructor. - */ - private int init(final ClassWriter cw, final int t) { - int s; - if (t == UNINITIALIZED_THIS) { - s = OBJECT | cw.addType(cw.thisName); - } else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) { - String type = cw.typeTable[t & BASE_VALUE].strVal1; - s = OBJECT | cw.addType(type); - } else { - return t; - } - for (int j = 0; j < initializationCount; ++j) { - int u = initializations[j]; - int dim = u & DIM; - int kind = u & KIND; - if (kind == LOCAL) { - u = dim + inputLocals[u & VALUE]; - } else if (kind == STACK) { - u = dim + inputStack[inputStack.length - (u & VALUE)]; - } - if (t == u) { - return s; - } - } - return t; - } - - /** - * Initializes the input frame of the first basic block from the method - * descriptor. - * - * @param cw - * the ClassWriter to which this label belongs. - * @param access - * the access flags of the method to which this label belongs. - * @param args - * the formal parameter types of this method. - * @param maxLocals - * the maximum number of local variables of this method. - */ - void initInputFrame(final ClassWriter cw, final int access, - final Type[] args, final int maxLocals) { - inputLocals = new int[maxLocals]; - inputStack = new int[0]; - int i = 0; - if ((access & Opcodes.ACC_STATIC) == 0) { - if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) { - inputLocals[i++] = OBJECT | cw.addType(cw.thisName); - } else { - inputLocals[i++] = UNINITIALIZED_THIS; - } - } - for (int j = 0; j < args.length; ++j) { - int t = type(cw, args[j].getDescriptor()); - inputLocals[i++] = t; - if (t == LONG || t == DOUBLE) { - inputLocals[i++] = TOP; - } - } - while (i < maxLocals) { - inputLocals[i++] = TOP; - } - } - - /** - * Simulates the action of the given instruction on the output stack frame. - * - * @param opcode - * the opcode of the instruction. - * @param arg - * the operand of the instruction, if any. - * @param cw - * the class writer to which this label belongs. - * @param item - * the operand of the instructions, if any. - */ - void execute(final int opcode, final int arg, final ClassWriter cw, - final Item item) { - int t1, t2, t3, t4; - switch (opcode) { - case Opcodes.NOP: - case Opcodes.INEG: - case Opcodes.LNEG: - case Opcodes.FNEG: - case Opcodes.DNEG: - case Opcodes.I2B: - case Opcodes.I2C: - case Opcodes.I2S: - case Opcodes.GOTO: - case Opcodes.RETURN: - break; - case Opcodes.ACONST_NULL: - push(NULL); - break; - case Opcodes.ICONST_M1: - case Opcodes.ICONST_0: - case Opcodes.ICONST_1: - case Opcodes.ICONST_2: - case Opcodes.ICONST_3: - case Opcodes.ICONST_4: - case Opcodes.ICONST_5: - case Opcodes.BIPUSH: - case Opcodes.SIPUSH: - case Opcodes.ILOAD: - push(INTEGER); - break; - case Opcodes.LCONST_0: - case Opcodes.LCONST_1: - case Opcodes.LLOAD: - push(LONG); - push(TOP); - break; - case Opcodes.FCONST_0: - case Opcodes.FCONST_1: - case Opcodes.FCONST_2: - case Opcodes.FLOAD: - push(FLOAT); - break; - case Opcodes.DCONST_0: - case Opcodes.DCONST_1: - case Opcodes.DLOAD: - push(DOUBLE); - push(TOP); - break; - case Opcodes.LDC: - switch (item.type) { - case ClassWriter.INT: - push(INTEGER); - break; - case ClassWriter.LONG: - push(LONG); - push(TOP); - break; - case ClassWriter.FLOAT: - push(FLOAT); - break; - case ClassWriter.DOUBLE: - push(DOUBLE); - push(TOP); - break; - case ClassWriter.CLASS: - push(OBJECT | cw.addType("java/lang/Class")); - break; - case ClassWriter.STR: - push(OBJECT | cw.addType("java/lang/String")); - break; - case ClassWriter.MTYPE: - push(OBJECT | cw.addType("java/lang/invoke/MethodType")); - break; - // case ClassWriter.HANDLE_BASE + [1..9]: - default: - push(OBJECT | cw.addType("java/lang/invoke/MethodHandle")); - } - break; - case Opcodes.ALOAD: - push(get(arg)); - break; - case Opcodes.IALOAD: - case Opcodes.BALOAD: - case Opcodes.CALOAD: - case Opcodes.SALOAD: - pop(2); - push(INTEGER); - break; - case Opcodes.LALOAD: - case Opcodes.D2L: - pop(2); - push(LONG); - push(TOP); - break; - case Opcodes.FALOAD: - pop(2); - push(FLOAT); - break; - case Opcodes.DALOAD: - case Opcodes.L2D: - pop(2); - push(DOUBLE); - push(TOP); - break; - case Opcodes.AALOAD: - pop(1); - t1 = pop(); - push(ELEMENT_OF + t1); - break; - case Opcodes.ISTORE: - case Opcodes.FSTORE: - case Opcodes.ASTORE: - t1 = pop(); - set(arg, t1); - if (arg > 0) { - t2 = get(arg - 1); - // if t2 is of kind STACK or LOCAL we cannot know its size! - if (t2 == LONG || t2 == DOUBLE) { - set(arg - 1, TOP); - } else if ((t2 & KIND) != BASE) { - set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); - } - } - break; - case Opcodes.LSTORE: - case Opcodes.DSTORE: - pop(1); - t1 = pop(); - set(arg, t1); - set(arg + 1, TOP); - if (arg > 0) { - t2 = get(arg - 1); - // if t2 is of kind STACK or LOCAL we cannot know its size! - if (t2 == LONG || t2 == DOUBLE) { - set(arg - 1, TOP); - } else if ((t2 & KIND) != BASE) { - set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); - } - } - break; - case Opcodes.IASTORE: - case Opcodes.BASTORE: - case Opcodes.CASTORE: - case Opcodes.SASTORE: - case Opcodes.FASTORE: - case Opcodes.AASTORE: - pop(3); - break; - case Opcodes.LASTORE: - case Opcodes.DASTORE: - pop(4); - break; - case Opcodes.POP: - case Opcodes.IFEQ: - case Opcodes.IFNE: - case Opcodes.IFLT: - case Opcodes.IFGE: - case Opcodes.IFGT: - case Opcodes.IFLE: - case Opcodes.IRETURN: - case Opcodes.FRETURN: - case Opcodes.ARETURN: - case Opcodes.TABLESWITCH: - case Opcodes.LOOKUPSWITCH: - case Opcodes.ATHROW: - case Opcodes.MONITORENTER: - case Opcodes.MONITOREXIT: - case Opcodes.IFNULL: - case Opcodes.IFNONNULL: - pop(1); - break; - case Opcodes.POP2: - case Opcodes.IF_ICMPEQ: - case Opcodes.IF_ICMPNE: - case Opcodes.IF_ICMPLT: - case Opcodes.IF_ICMPGE: - case Opcodes.IF_ICMPGT: - case Opcodes.IF_ICMPLE: - case Opcodes.IF_ACMPEQ: - case Opcodes.IF_ACMPNE: - case Opcodes.LRETURN: - case Opcodes.DRETURN: - pop(2); - break; - case Opcodes.DUP: - t1 = pop(); - push(t1); - push(t1); - break; - case Opcodes.DUP_X1: - t1 = pop(); - t2 = pop(); - push(t1); - push(t2); - push(t1); - break; - case Opcodes.DUP_X2: - t1 = pop(); - t2 = pop(); - t3 = pop(); - push(t1); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.DUP2: - t1 = pop(); - t2 = pop(); - push(t2); - push(t1); - push(t2); - push(t1); - break; - case Opcodes.DUP2_X1: - t1 = pop(); - t2 = pop(); - t3 = pop(); - push(t2); - push(t1); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.DUP2_X2: - t1 = pop(); - t2 = pop(); - t3 = pop(); - t4 = pop(); - push(t2); - push(t1); - push(t4); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.SWAP: - t1 = pop(); - t2 = pop(); - push(t1); - push(t2); - break; - case Opcodes.IADD: - case Opcodes.ISUB: - case Opcodes.IMUL: - case Opcodes.IDIV: - case Opcodes.IREM: - case Opcodes.IAND: - case Opcodes.IOR: - case Opcodes.IXOR: - case Opcodes.ISHL: - case Opcodes.ISHR: - case Opcodes.IUSHR: - case Opcodes.L2I: - case Opcodes.D2I: - case Opcodes.FCMPL: - case Opcodes.FCMPG: - pop(2); - push(INTEGER); - break; - case Opcodes.LADD: - case Opcodes.LSUB: - case Opcodes.LMUL: - case Opcodes.LDIV: - case Opcodes.LREM: - case Opcodes.LAND: - case Opcodes.LOR: - case Opcodes.LXOR: - pop(4); - push(LONG); - push(TOP); - break; - case Opcodes.FADD: - case Opcodes.FSUB: - case Opcodes.FMUL: - case Opcodes.FDIV: - case Opcodes.FREM: - case Opcodes.L2F: - case Opcodes.D2F: - pop(2); - push(FLOAT); - break; - case Opcodes.DADD: - case Opcodes.DSUB: - case Opcodes.DMUL: - case Opcodes.DDIV: - case Opcodes.DREM: - pop(4); - push(DOUBLE); - push(TOP); - break; - case Opcodes.LSHL: - case Opcodes.LSHR: - case Opcodes.LUSHR: - pop(3); - push(LONG); - push(TOP); - break; - case Opcodes.IINC: - set(arg, INTEGER); - break; - case Opcodes.I2L: - case Opcodes.F2L: - pop(1); - push(LONG); - push(TOP); - break; - case Opcodes.I2F: - pop(1); - push(FLOAT); - break; - case Opcodes.I2D: - case Opcodes.F2D: - pop(1); - push(DOUBLE); - push(TOP); - break; - case Opcodes.F2I: - case Opcodes.ARRAYLENGTH: - case Opcodes.INSTANCEOF: - pop(1); - push(INTEGER); - break; - case Opcodes.LCMP: - case Opcodes.DCMPL: - case Opcodes.DCMPG: - pop(4); - push(INTEGER); - break; - case Opcodes.JSR: - case Opcodes.RET: - throw new RuntimeException( - "JSR/RET are not supported with computeFrames option"); - case Opcodes.GETSTATIC: - push(cw, item.strVal3); - break; - case Opcodes.PUTSTATIC: - pop(item.strVal3); - break; - case Opcodes.GETFIELD: - pop(1); - push(cw, item.strVal3); - break; - case Opcodes.PUTFIELD: - pop(item.strVal3); - pop(); - break; - case Opcodes.INVOKEVIRTUAL: - case Opcodes.INVOKESPECIAL: - case Opcodes.INVOKESTATIC: - case Opcodes.INVOKEINTERFACE: - pop(item.strVal3); - if (opcode != Opcodes.INVOKESTATIC) { - t1 = pop(); - if (opcode == Opcodes.INVOKESPECIAL - && item.strVal2.charAt(0) == '<') { - init(t1); - } - } - push(cw, item.strVal3); - break; - case Opcodes.INVOKEDYNAMIC: - pop(item.strVal2); - push(cw, item.strVal2); - break; - case Opcodes.NEW: - push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg)); - break; - case Opcodes.NEWARRAY: - pop(); - switch (arg) { - case Opcodes.T_BOOLEAN: - push(ARRAY_OF | BOOLEAN); - break; - case Opcodes.T_CHAR: - push(ARRAY_OF | CHAR); - break; - case Opcodes.T_BYTE: - push(ARRAY_OF | BYTE); - break; - case Opcodes.T_SHORT: - push(ARRAY_OF | SHORT); - break; - case Opcodes.T_INT: - push(ARRAY_OF | INTEGER); - break; - case Opcodes.T_FLOAT: - push(ARRAY_OF | FLOAT); - break; - case Opcodes.T_DOUBLE: - push(ARRAY_OF | DOUBLE); - break; - // case Opcodes.T_LONG: - default: - push(ARRAY_OF | LONG); - break; - } - break; - case Opcodes.ANEWARRAY: - String s = item.strVal1; - pop(); - if (s.charAt(0) == '[') { - push(cw, '[' + s); - } else { - push(ARRAY_OF | OBJECT | cw.addType(s)); - } - break; - case Opcodes.CHECKCAST: - s = item.strVal1; - pop(); - if (s.charAt(0) == '[') { - push(cw, s); - } else { - push(OBJECT | cw.addType(s)); - } - break; - // case Opcodes.MULTIANEWARRAY: - default: - pop(arg); - push(cw, item.strVal1); - break; - } - } - - /** - * Merges the input frame of the given basic block with the input and output - * frames of this basic block. Returns true if the input frame of - * the given label has been changed by this operation. - * - * @param cw - * the ClassWriter to which this label belongs. - * @param frame - * the basic block whose input frame must be updated. - * @param edge - * the kind of the {@link Edge} between this label and 'label'. - * See {@link Edge#info}. - * @return true if the input frame of the given label has been - * changed by this operation. - */ - boolean merge(final ClassWriter cw, final Frame frame, final int edge) { - boolean changed = false; - int i, s, dim, kind, t; - - int nLocal = inputLocals.length; - int nStack = inputStack.length; - if (frame.inputLocals == null) { - frame.inputLocals = new int[nLocal]; - changed = true; - } - - for (i = 0; i < nLocal; ++i) { - if (outputLocals != null && i < outputLocals.length) { - s = outputLocals[i]; - if (s == 0) { - t = inputLocals[i]; - } else { - dim = s & DIM; - kind = s & KIND; - if (kind == BASE) { - t = s; - } else { - if (kind == LOCAL) { - t = dim + inputLocals[s & VALUE]; - } else { - t = dim + inputStack[nStack - (s & VALUE)]; - } - if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 - && (t == LONG || t == DOUBLE)) { - t = TOP; - } - } - } - } else { - t = inputLocals[i]; - } - if (initializations != null) { - t = init(cw, t); - } - changed |= merge(cw, t, frame.inputLocals, i); - } - - if (edge > 0) { - for (i = 0; i < nLocal; ++i) { - t = inputLocals[i]; - changed |= merge(cw, t, frame.inputLocals, i); - } - if (frame.inputStack == null) { - frame.inputStack = new int[1]; - changed = true; - } - changed |= merge(cw, edge, frame.inputStack, 0); - return changed; - } - - int nInputStack = inputStack.length + owner.inputStackTop; - if (frame.inputStack == null) { - frame.inputStack = new int[nInputStack + outputStackTop]; - changed = true; - } - - for (i = 0; i < nInputStack; ++i) { - t = inputStack[i]; - if (initializations != null) { - t = init(cw, t); - } - changed |= merge(cw, t, frame.inputStack, i); - } - for (i = 0; i < outputStackTop; ++i) { - s = outputStack[i]; - dim = s & DIM; - kind = s & KIND; - if (kind == BASE) { - t = s; - } else { - if (kind == LOCAL) { - t = dim + inputLocals[s & VALUE]; - } else { - t = dim + inputStack[nStack - (s & VALUE)]; - } - if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 - && (t == LONG || t == DOUBLE)) { - t = TOP; - } - } - if (initializations != null) { - t = init(cw, t); - } - changed |= merge(cw, t, frame.inputStack, nInputStack + i); - } - return changed; - } - - /** - * Merges the type at the given index in the given type array with the given - * type. Returns true if the type array has been modified by this - * operation. - * - * @param cw - * the ClassWriter to which this label belongs. - * @param t - * the type with which the type array element must be merged. - * @param types - * an array of types. - * @param index - * the index of the type that must be merged in 'types'. - * @return true if the type array has been modified by this - * operation. - */ - private static boolean merge(final ClassWriter cw, int t, - final int[] types, final int index) { - int u = types[index]; - if (u == t) { - // if the types are equal, merge(u,t)=u, so there is no change - return false; - } - if ((t & ~DIM) == NULL) { - if (u == NULL) { - return false; - } - t = NULL; - } - if (u == 0) { - // if types[index] has never been assigned, merge(u,t)=t - types[index] = t; - return true; - } - int v; - if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) { - // if u is a reference type of any dimension - if (t == NULL) { - // if t is the NULL type, merge(u,t)=u, so there is no change - return false; - } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) { - // if t and u have the same dimension and same base kind - if ((u & BASE_KIND) == OBJECT) { - // if t is also a reference type, and if u and t have the - // same dimension merge(u,t) = dim(t) | common parent of the - // element types of u and t - v = (t & DIM) | OBJECT - | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); - } else { - // if u and t are array types, but not with the same element - // type, merge(u,t) = dim(u) - 1 | java/lang/Object - int vdim = ELEMENT_OF + (u & DIM); - v = vdim | OBJECT | cw.addType("java/lang/Object"); - } - } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) { - // if t is any other reference or array type, the merged type - // is min(udim, tdim) | java/lang/Object, where udim is the - // array dimension of u, minus 1 if u is an array type with a - // primitive element type (and similarly for tdim). - int tdim = (((t & DIM) == 0 || (t & BASE_KIND) == OBJECT) ? 0 - : ELEMENT_OF) + (t & DIM); - int udim = (((u & DIM) == 0 || (u & BASE_KIND) == OBJECT) ? 0 - : ELEMENT_OF) + (u & DIM); - v = Math.min(tdim, udim) | OBJECT - | cw.addType("java/lang/Object"); - } else { - // if t is any other type, merge(u,t)=TOP - v = TOP; - } - } else if (u == NULL) { - // if u is the NULL type, merge(u,t)=t, - // or TOP if t is not a reference type - v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP; - } else { - // if u is any other type, merge(u,t)=TOP whatever t - v = TOP; - } - if (u != v) { - types[index] = v; - return true; - } - return false; - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Handle.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Handle.java deleted file mode 100644 index 99fec0d9..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Handle.java +++ /dev/null @@ -1,170 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A reference to a field or a method. - * - * @author Remi Forax - * @author Eric Bruneton - */ -public final class Handle { - - /** - * The kind of field or method designated by this Handle. Should be - * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, - * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, - * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or - * {@link Opcodes#H_INVOKEINTERFACE}. - */ - final int tag; - - /** - * The internal name of the class that owns the field or method designated - * by this handle. - */ - final String owner; - - /** - * The name of the field or method designated by this handle. - */ - final String name; - - /** - * The descriptor of the field or method designated by this handle. - */ - final String desc; - - /** - * Constructs a new field or method handle. - * - * @param tag - * the kind of field or method designated by this Handle. Must be - * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, - * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, - * {@link Opcodes#H_INVOKEVIRTUAL}, - * {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or - * {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner - * the internal name of the class that owns the field or method - * designated by this handle. - * @param name - * the name of the field or method designated by this handle. - * @param desc - * the descriptor of the field or method designated by this - * handle. - */ - public Handle(int tag, String owner, String name, String desc) { - this.tag = tag; - this.owner = owner; - this.name = name; - this.desc = desc; - } - - /** - * Returns the kind of field or method designated by this handle. - * - * @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, - * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, - * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or - * {@link Opcodes#H_INVOKEINTERFACE}. - */ - public int getTag() { - return tag; - } - - /** - * Returns the internal name of the class that owns the field or method - * designated by this handle. - * - * @return the internal name of the class that owns the field or method - * designated by this handle. - */ - public String getOwner() { - return owner; - } - - /** - * Returns the name of the field or method designated by this handle. - * - * @return the name of the field or method designated by this handle. - */ - public String getName() { - return name; - } - - /** - * Returns the descriptor of the field or method designated by this handle. - * - * @return the descriptor of the field or method designated by this handle. - */ - public String getDesc() { - return desc; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Handle)) { - return false; - } - Handle h = (Handle) obj; - return tag == h.tag && owner.equals(h.owner) && name.equals(h.name) - && desc.equals(h.desc); - } - - @Override - public int hashCode() { - return tag + owner.hashCode() * name.hashCode() * desc.hashCode(); - } - - /** - * Returns the textual representation of this handle. The textual - * representation is: - * - *
-     * owner '.' name desc ' ' '(' tag ')'
-     * 
- * - * . As this format is unambiguous, it can be parsed if necessary. - */ - @Override - public String toString() { - return owner + '.' + name + desc + " (" + tag + ')'; - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Handler.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Handler.java deleted file mode 100644 index ce0c956d..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Handler.java +++ /dev/null @@ -1,121 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * Information about an exception handler block. - * - * @author Eric Bruneton - */ -class Handler { - - /** - * Beginning of the exception handler's scope (inclusive). - */ - Label start; - - /** - * End of the exception handler's scope (exclusive). - */ - Label end; - - /** - * Beginning of the exception handler's code. - */ - Label handler; - - /** - * Internal name of the type of exceptions handled by this handler, or - * null to catch any exceptions. - */ - String desc; - - /** - * Constant pool index of the internal name of the type of exceptions - * handled by this handler, or 0 to catch any exceptions. - */ - int type; - - /** - * Next exception handler block info. - */ - Handler next; - - /** - * Removes the range between start and end from the given exception - * handlers. - * - * @param h - * an exception handler list. - * @param start - * the start of the range to be removed. - * @param end - * the end of the range to be removed. Maybe null. - * @return the exception handler list with the start-end range removed. - */ - static Handler remove(Handler h, Label start, Label end) { - if (h == null) { - return null; - } else { - h.next = remove(h.next, start, end); - } - int hstart = h.start.position; - int hend = h.end.position; - int s = start.position; - int e = end == null ? Integer.MAX_VALUE : end.position; - // if [hstart,hend[ and [s,e[ intervals intersect... - if (s < hend && e > hstart) { - if (s <= hstart) { - if (e >= hend) { - // [hstart,hend[ fully included in [s,e[, h removed - h = h.next; - } else { - // [hstart,hend[ minus [s,e[ = [e,hend[ - h.start = end; - } - } else if (e >= hend) { - // [hstart,hend[ minus [s,e[ = [hstart,s[ - h.end = start; - } else { - // [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[ - Handler g = new Handler(); - g.start = end; - g.end = h.end; - g.handler = h.handler; - g.desc = h.desc; - g.type = h.type; - g.next = h.next; - h.end = start; - h.next = g; - } - } - return h; - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Item.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Item.java deleted file mode 100644 index 339082f9..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Item.java +++ /dev/null @@ -1,313 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A constant pool item. Constant pool items can be created with the 'newXXX' - * methods in the {@link ClassWriter} class. - * - * @author Eric Bruneton - */ -final class Item { - - /** - * Index of this item in the constant pool. - */ - int index; - - /** - * Type of this constant pool item. A single class is used to represent all - * constant pool item types, in order to minimize the bytecode size of this - * package. The value of this field is one of {@link ClassWriter#INT}, - * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT}, - * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8}, - * {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, - * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, - * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}, - * {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}. - * - * MethodHandle constant 9 variations are stored using a range of 9 values - * from {@link ClassWriter#HANDLE_BASE} + 1 to - * {@link ClassWriter#HANDLE_BASE} + 9. - * - * Special Item types are used for Items that are stored in the ClassWriter - * {@link ClassWriter#typeTable}, instead of the constant pool, in order to - * avoid clashes with normal constant pool items in the ClassWriter constant - * pool's hash table. These special item types are - * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and - * {@link ClassWriter#TYPE_MERGED}. - */ - int type; - - /** - * Value of this item, for an integer item. - */ - int intVal; - - /** - * Value of this item, for a long item. - */ - long longVal; - - /** - * First part of the value of this item, for items that do not hold a - * primitive value. - */ - String strVal1; - - /** - * Second part of the value of this item, for items that do not hold a - * primitive value. - */ - String strVal2; - - /** - * Third part of the value of this item, for items that do not hold a - * primitive value. - */ - String strVal3; - - /** - * The hash code value of this constant pool item. - */ - int hashCode; - - /** - * Link to another constant pool item, used for collision lists in the - * constant pool's hash table. - */ - Item next; - - /** - * Constructs an uninitialized {@link Item}. - */ - Item() { - } - - /** - * Constructs an uninitialized {@link Item} for constant pool element at - * given position. - * - * @param index - * index of the item to be constructed. - */ - Item(final int index) { - this.index = index; - } - - /** - * Constructs a copy of the given item. - * - * @param index - * index of the item to be constructed. - * @param i - * the item that must be copied into the item to be constructed. - */ - Item(final int index, final Item i) { - this.index = index; - type = i.type; - intVal = i.intVal; - longVal = i.longVal; - strVal1 = i.strVal1; - strVal2 = i.strVal2; - strVal3 = i.strVal3; - hashCode = i.hashCode; - } - - /** - * Sets this item to an integer item. - * - * @param intVal - * the value of this item. - */ - void set(final int intVal) { - this.type = ClassWriter.INT; - this.intVal = intVal; - this.hashCode = 0x7FFFFFFF & (type + intVal); - } - - /** - * Sets this item to a long item. - * - * @param longVal - * the value of this item. - */ - void set(final long longVal) { - this.type = ClassWriter.LONG; - this.longVal = longVal; - this.hashCode = 0x7FFFFFFF & (type + (int) longVal); - } - - /** - * Sets this item to a float item. - * - * @param floatVal - * the value of this item. - */ - void set(final float floatVal) { - this.type = ClassWriter.FLOAT; - this.intVal = Float.floatToRawIntBits(floatVal); - this.hashCode = 0x7FFFFFFF & (type + (int) floatVal); - } - - /** - * Sets this item to a double item. - * - * @param doubleVal - * the value of this item. - */ - void set(final double doubleVal) { - this.type = ClassWriter.DOUBLE; - this.longVal = Double.doubleToRawLongBits(doubleVal); - this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal); - } - - /** - * Sets this item to an item that do not hold a primitive value. - * - * @param type - * the type of this item. - * @param strVal1 - * first part of the value of this item. - * @param strVal2 - * second part of the value of this item. - * @param strVal3 - * third part of the value of this item. - */ - @SuppressWarnings("fallthrough") - void set(final int type, final String strVal1, final String strVal2, - final String strVal3) { - this.type = type; - this.strVal1 = strVal1; - this.strVal2 = strVal2; - this.strVal3 = strVal3; - switch (type) { - case ClassWriter.CLASS: - this.intVal = 0; // intVal of a class must be zero, see visitInnerClass - case ClassWriter.UTF8: - case ClassWriter.STR: - case ClassWriter.MTYPE: - case ClassWriter.TYPE_NORMAL: - hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); - return; - case ClassWriter.NAME_TYPE: { - hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() - * strVal2.hashCode()); - return; - } - // ClassWriter.FIELD: - // ClassWriter.METH: - // ClassWriter.IMETH: - // ClassWriter.HANDLE_BASE + 1..9 - default: - hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() - * strVal2.hashCode() * strVal3.hashCode()); - } - } - - /** - * Sets the item to an InvokeDynamic item. - * - * @param name - * invokedynamic's name. - * @param desc - * invokedynamic's desc. - * @param bsmIndex - * zero based index into the class attribute BootrapMethods. - */ - void set(String name, String desc, int bsmIndex) { - this.type = ClassWriter.INDY; - this.longVal = bsmIndex; - this.strVal1 = name; - this.strVal2 = desc; - this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex - * strVal1.hashCode() * strVal2.hashCode()); - } - - /** - * Sets the item to a BootstrapMethod item. - * - * @param position - * position in byte in the class attribute BootrapMethods. - * @param hashCode - * hashcode of the item. This hashcode is processed from the - * hashcode of the bootstrap method and the hashcode of all - * bootstrap arguments. - */ - void set(int position, int hashCode) { - this.type = ClassWriter.BSM; - this.intVal = position; - this.hashCode = hashCode; - } - - /** - * Indicates if the given item is equal to this one. This method assumes - * that the two items have the same {@link #type}. - * - * @param i - * the item to be compared to this one. Both items must have the - * same {@link #type}. - * @return true if the given item if equal to this one, - * false otherwise. - */ - boolean isEqualTo(final Item i) { - switch (type) { - case ClassWriter.UTF8: - case ClassWriter.STR: - case ClassWriter.CLASS: - case ClassWriter.MTYPE: - case ClassWriter.TYPE_NORMAL: - return i.strVal1.equals(strVal1); - case ClassWriter.TYPE_MERGED: - case ClassWriter.LONG: - case ClassWriter.DOUBLE: - return i.longVal == longVal; - case ClassWriter.INT: - case ClassWriter.FLOAT: - return i.intVal == intVal; - case ClassWriter.TYPE_UNINIT: - return i.intVal == intVal && i.strVal1.equals(strVal1); - case ClassWriter.NAME_TYPE: - return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2); - case ClassWriter.INDY: { - return i.longVal == longVal && i.strVal1.equals(strVal1) - && i.strVal2.equals(strVal2); - } - // case ClassWriter.FIELD: - // case ClassWriter.METH: - // case ClassWriter.IMETH: - // case ClassWriter.HANDLE_BASE + 1..9 - default: - return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2) - && i.strVal3.equals(strVal3); - } - } - -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Label.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Label.java deleted file mode 100644 index 7c2b5842..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Label.java +++ /dev/null @@ -1,565 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A label represents a position in the bytecode of a method. Labels are used - * for jump, goto, and switch instructions, and for try catch blocks. A label - * designates the instruction that is just after. Note however that there - * can be other elements between a label and the instruction it designates (such - * as other labels, stack map frames, line numbers, etc.). - * - * @author Eric Bruneton - */ -public class Label { - - /** - * Indicates if this label is only used for debug attributes. Such a label - * is not the start of a basic block, the target of a jump instruction, or - * an exception handler. It can be safely ignored in control flow graph - * analysis algorithms (for optimization purposes). - */ - static final int DEBUG = 1; - - /** - * Indicates if the position of this label is known. - */ - static final int RESOLVED = 2; - - /** - * Indicates if this label has been updated, after instruction resizing. - */ - static final int RESIZED = 4; - - /** - * Indicates if this basic block has been pushed in the basic block stack. - * See {@link MethodWriter#visitMaxs visitMaxs}. - */ - static final int PUSHED = 8; - - /** - * Indicates if this label is the target of a jump instruction, or the start - * of an exception handler. - */ - static final int TARGET = 16; - - /** - * Indicates if a stack map frame must be stored for this label. - */ - static final int STORE = 32; - - /** - * Indicates if this label corresponds to a reachable basic block. - */ - static final int REACHABLE = 64; - - /** - * Indicates if this basic block ends with a JSR instruction. - */ - static final int JSR = 128; - - /** - * Indicates if this basic block ends with a RET instruction. - */ - static final int RET = 256; - - /** - * Indicates if this basic block is the start of a subroutine. - */ - static final int SUBROUTINE = 512; - - /** - * Indicates if this subroutine basic block has been visited by a - * visitSubroutine(null, ...) call. - */ - static final int VISITED = 1024; - - /** - * Indicates if this subroutine basic block has been visited by a - * visitSubroutine(!null, ...) call. - */ - static final int VISITED2 = 2048; - - /** - * Field used to associate user information to a label. Warning: this field - * is used by the ASM tree package. In order to use it with the ASM tree - * package you must override the - * {@link com.javadeobfuscator.deobfuscator.org.objectweb.asm.tree.MethodNode#getLabelNode} method. - */ - public Object info; - - /** - * Flags that indicate the status of this label. - * - * @see #DEBUG - * @see #RESOLVED - * @see #RESIZED - * @see #PUSHED - * @see #TARGET - * @see #STORE - * @see #REACHABLE - * @see #JSR - * @see #RET - */ - int status; - - /** - * The line number corresponding to this label, if known. If there are - * several lines, each line is stored in a separate label, all linked via - * their next field (these links are created in ClassReader and removed just - * before visitLabel is called, so that this does not impact the rest of the - * code). - */ - int line; - - /** - * The position of this label in the code, if known. - */ - int position; - - /** - * Number of forward references to this label, times two. - */ - private int referenceCount; - - /** - * Informations about forward references. Each forward reference is - * described by two consecutive integers in this array: the first one is the - * position of the first byte of the bytecode instruction that contains the - * forward reference, while the second is the position of the first byte of - * the forward reference itself. In fact the sign of the first integer - * indicates if this reference uses 2 or 4 bytes, and its absolute value - * gives the position of the bytecode instruction. This array is also used - * as a bitset to store the subroutines to which a basic block belongs. This - * information is needed in {@linked MethodWriter#visitMaxs}, after all - * forward references have been resolved. Hence the same array can be used - * for both purposes without problems. - */ - private int[] srcAndRefPositions; - - // ------------------------------------------------------------------------ - - /* - * Fields for the control flow and data flow graph analysis algorithms (used - * to compute the maximum stack size or the stack map frames). A control - * flow graph contains one node per "basic block", and one edge per "jump" - * from one basic block to another. Each node (i.e., each basic block) is - * represented by the Label object that corresponds to the first instruction - * of this basic block. Each node also stores the list of its successors in - * the graph, as a linked list of Edge objects. - * - * The control flow analysis algorithms used to compute the maximum stack - * size or the stack map frames are similar and use two steps. The first - * step, during the visit of each instruction, builds information about the - * state of the local variables and the operand stack at the end of each - * basic block, called the "output frame", relatively to the frame - * state at the beginning of the basic block, which is called the "input - * frame", and which is unknown during this step. The second step, in - * {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes - * information about the input frame of each basic block, from the input - * state of the first basic block (known from the method signature), and by - * the using the previously computed relative output frames. - * - * The algorithm used to compute the maximum stack size only computes the - * relative output and absolute input stack heights, while the algorithm - * used to compute stack map frames computes relative output frames and - * absolute input frames. - */ - - /** - * Start of the output stack relatively to the input stack. The exact - * semantics of this field depends on the algorithm that is used. - * - * When only the maximum stack size is computed, this field is the number of - * elements in the input stack. - * - * When the stack map frames are completely computed, this field is the - * offset of the first output stack element relatively to the top of the - * input stack. This offset is always negative or null. A null offset means - * that the output stack must be appended to the input stack. A -n offset - * means that the first n output stack elements must replace the top n input - * stack elements, and that the other elements must be appended to the input - * stack. - */ - int inputStackTop; - - /** - * Maximum height reached by the output stack, relatively to the top of the - * input stack. This maximum is always positive or null. - */ - int outputStackMax; - - /** - * Information about the input and output stack map frames of this basic - * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} - * option is used. - */ - Frame frame; - - /** - * The successor of this label, in the order they are visited. This linked - * list does not include labels used for debug info only. If - * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it - * does not contain successive labels that denote the same bytecode position - * (in this case only the first label appears in this list). - */ - Label successor; - - /** - * The successors of this node in the control flow graph. These successors - * are stored in a linked list of {@link Edge Edge} objects, linked to each - * other by their {@link Edge#next} field. - */ - Edge successors; - - /** - * The next basic block in the basic block stack. This stack is used in the - * main loop of the fix point algorithm used in the second step of the - * control flow analysis algorithms. It is also used in - * {@link #visitSubroutine} to avoid using a recursive method, and in - * ClassReader to temporarily store multiple source lines for a label. - * - * @see MethodWriter#visitMaxs - */ - Label next; - - // ------------------------------------------------------------------------ - // Constructor - // ------------------------------------------------------------------------ - - /** - * Constructs a new label. - */ - public Label() { - } - - // ------------------------------------------------------------------------ - // Methods to compute offsets and to manage forward references - // ------------------------------------------------------------------------ - - /** - * Returns the offset corresponding to this label. This offset is computed - * from the start of the method's bytecode. This method is intended for - * {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @return the offset corresponding to this label. - * @throws IllegalStateException - * if this label is not resolved yet. - */ - public int getOffset() { - if ((status & RESOLVED) == 0) { - throw new IllegalStateException( - "Label offset position has not been resolved yet"); - } - return position; - } - - /** - * Puts a reference to this label in the bytecode of a method. If the - * position of the label is known, the offset is computed and written - * directly. Otherwise, a null offset is written and a new forward reference - * is declared for this label. - * - * @param owner - * the code writer that calls this method. - * @param out - * the bytecode of the method. - * @param source - * the position of first byte of the bytecode instruction that - * contains this label. - * @param wideOffset - * true if the reference must be stored in 4 bytes, or - * false if it must be stored with 2 bytes. - * @throws IllegalArgumentException - * if this label has not been created by the given code writer. - */ - void put(final MethodWriter owner, final ByteVector out, final int source, - final boolean wideOffset) { - if ((status & RESOLVED) == 0) { - if (wideOffset) { - addReference(-1 - source, out.length); - out.putInt(-1); - } else { - addReference(source, out.length); - out.putShort(-1); - } - } else { - if (wideOffset) { - out.putInt(position - source); - } else { - out.putShort(position - source); - } - } - } - - /** - * Adds a forward reference to this label. This method must be called only - * for a true forward reference, i.e. only if this label is not resolved - * yet. For backward references, the offset of the reference can be, and - * must be, computed and stored directly. - * - * @param sourcePosition - * the position of the referencing instruction. This position - * will be used to compute the offset of this forward reference. - * @param referencePosition - * the position where the offset for this forward reference must - * be stored. - */ - private void addReference(final int sourcePosition, - final int referencePosition) { - if (srcAndRefPositions == null) { - srcAndRefPositions = new int[6]; - } - if (referenceCount >= srcAndRefPositions.length) { - int[] a = new int[srcAndRefPositions.length + 6]; - System.arraycopy(srcAndRefPositions, 0, a, 0, - srcAndRefPositions.length); - srcAndRefPositions = a; - } - srcAndRefPositions[referenceCount++] = sourcePosition; - srcAndRefPositions[referenceCount++] = referencePosition; - } - - /** - * Resolves all forward references to this label. This method must be called - * when this label is added to the bytecode of the method, i.e. when its - * position becomes known. This method fills in the blanks that where left - * in the bytecode by each forward reference previously added to this label. - * - * @param owner - * the code writer that calls this method. - * @param position - * the position of this label in the bytecode. - * @param data - * the bytecode of the method. - * @return true if a blank that was left for this label was to - * small to store the offset. In such a case the corresponding jump - * instruction is replaced with a pseudo instruction (using unused - * opcodes) using an unsigned two bytes offset. These pseudo - * instructions will need to be replaced with true instructions with - * wider offsets (4 bytes instead of 2). This is done in - * {@link MethodWriter#resizeInstructions}. - * @throws IllegalArgumentException - * if this label has already been resolved, or if it has not - * been created by the given code writer. - */ - boolean resolve(final MethodWriter owner, final int position, - final byte[] data) { - boolean needUpdate = false; - this.status |= RESOLVED; - this.position = position; - int i = 0; - while (i < referenceCount) { - int source = srcAndRefPositions[i++]; - int reference = srcAndRefPositions[i++]; - int offset; - if (source >= 0) { - offset = position - source; - if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { - /* - * changes the opcode of the jump instruction, in order to - * be able to find it later (see resizeInstructions in - * MethodWriter). These temporary opcodes are similar to - * jump instruction opcodes, except that the 2 bytes offset - * is unsigned (and can therefore represent values from 0 to - * 65535, which is sufficient since the size of a method is - * limited to 65535 bytes). - */ - int opcode = data[reference - 1] & 0xFF; - if (opcode <= Opcodes.JSR) { - // changes IFEQ ... JSR to opcodes 202 to 217 - data[reference - 1] = (byte) (opcode + 49); - } else { - // changes IFNULL and IFNONNULL to opcodes 218 and 219 - data[reference - 1] = (byte) (opcode + 20); - } - needUpdate = true; - } - data[reference++] = (byte) (offset >>> 8); - data[reference] = (byte) offset; - } else { - offset = position + source + 1; - data[reference++] = (byte) (offset >>> 24); - data[reference++] = (byte) (offset >>> 16); - data[reference++] = (byte) (offset >>> 8); - data[reference] = (byte) offset; - } - } - return needUpdate; - } - - /** - * Returns the first label of the series to which this label belongs. For an - * isolated label or for the first label in a series of successive labels, - * this method returns the label itself. For other labels it returns the - * first label of the series. - * - * @return the first label of the series to which this label belongs. - */ - Label getFirst() { - return !ClassReader.FRAMES || frame == null ? this : frame.owner; - } - - // ------------------------------------------------------------------------ - // Methods related to subroutines - // ------------------------------------------------------------------------ - - /** - * Returns true is this basic block belongs to the given subroutine. - * - * @param id - * a subroutine id. - * @return true is this basic block belongs to the given subroutine. - */ - boolean inSubroutine(final long id) { - if ((status & Label.VISITED) != 0) { - return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0; - } - return false; - } - - /** - * Returns true if this basic block and the given one belong to a common - * subroutine. - * - * @param block - * another basic block. - * @return true if this basic block and the given one belong to a common - * subroutine. - */ - boolean inSameSubroutine(final Label block) { - if ((status & VISITED) == 0 || (block.status & VISITED) == 0) { - return false; - } - for (int i = 0; i < srcAndRefPositions.length; ++i) { - if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) { - return true; - } - } - return false; - } - - /** - * Marks this basic block as belonging to the given subroutine. - * - * @param id - * a subroutine id. - * @param nbSubroutines - * the total number of subroutines in the method. - */ - void addToSubroutine(final long id, final int nbSubroutines) { - if ((status & VISITED) == 0) { - status |= VISITED; - srcAndRefPositions = new int[nbSubroutines / 32 + 1]; - } - srcAndRefPositions[(int) (id >>> 32)] |= (int) id; - } - - /** - * Finds the basic blocks that belong to a given subroutine, and marks these - * blocks as belonging to this subroutine. This method follows the control - * flow graph to find all the blocks that are reachable from the current - * block WITHOUT following any JSR target. - * - * @param JSR - * a JSR block that jumps to this subroutine. If this JSR is not - * null it is added to the successor of the RET blocks found in - * the subroutine. - * @param id - * the id of this subroutine. - * @param nbSubroutines - * the total number of subroutines in the method. - */ - void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) { - // user managed stack of labels, to avoid using a recursive method - // (recursivity can lead to stack overflow with very large methods) - Label stack = this; - while (stack != null) { - // removes a label l from the stack - Label l = stack; - stack = l.next; - l.next = null; - - if (JSR != null) { - if ((l.status & VISITED2) != 0) { - continue; - } - l.status |= VISITED2; - // adds JSR to the successors of l, if it is a RET block - if ((l.status & RET) != 0) { - if (!l.inSameSubroutine(JSR)) { - Edge e = new Edge(); - e.info = l.inputStackTop; - e.successor = JSR.successors.successor; - e.next = l.successors; - l.successors = e; - } - } - } else { - // if the l block already belongs to subroutine 'id', continue - if (l.inSubroutine(id)) { - continue; - } - // marks the l block as belonging to subroutine 'id' - l.addToSubroutine(id, nbSubroutines); - } - // pushes each successor of l on the stack, except JSR targets - Edge e = l.successors; - while (e != null) { - // if the l block is a JSR block, then 'l.successors.next' leads - // to the JSR target (see {@link #visitJumpInsn}) and must - // therefore not be followed - if ((l.status & Label.JSR) == 0 || e != l.successors.next) { - // pushes e.successor on the stack if it not already added - if (e.successor.next == null) { - e.successor.next = stack; - stack = e.successor; - } - } - e = e.next; - } - } - } - - // ------------------------------------------------------------------------ - // Overriden Object methods - // ------------------------------------------------------------------------ - - /** - * Returns a string representation of this label. - * - * @return a string representation of this label. - */ - @Override - public String toString() { - return "L" + System.identityHashCode(this); - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/MethodVisitor.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/MethodVisitor.java deleted file mode 100644 index 4b9d3093..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/MethodVisitor.java +++ /dev/null @@ -1,881 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A visitor to visit a Java method. The methods of this class must be called in - * the following order: ( visitParameter )* [ - * visitAnnotationDefault ] ( visitAnnotation | - * visitParameterAnnotation visitTypeAnnotation | - * visitAttribute )* [ visitCode ( visitFrame | - * visitXInsn | visitLabel | - * visitInsnAnnotation | visitTryCatchBlock | - * visitTryCatchAnnotation | visitLocalVariable | - * visitLocalVariableAnnotation | visitLineNumber )* - * visitMaxs ] visitEnd. In addition, the - * visitXInsn and visitLabel methods must be called in - * the sequential order of the bytecode instructions of the visited code, - * visitInsnAnnotation must be called after the annotated - * instruction, visitTryCatchBlock must be called before the - * labels passed as arguments have been visited, - * visitTryCatchBlockAnnotation must be called after the - * corresponding try catch block has been visited, and the - * visitLocalVariable, visitLocalVariableAnnotation and - * visitLineNumber methods must be called after the labels - * passed as arguments have been visited. - * - * @author Eric Bruneton - */ -public abstract class MethodVisitor { - - /** - * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - */ - protected final int api; - - /** - * The method visitor to which this visitor must delegate method calls. May - * be null. - */ - protected MethodVisitor mv; - - /** - * Constructs a new {@link MethodVisitor}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - */ - public MethodVisitor(final int api) { - this(api, null); - } - - /** - * Constructs a new {@link MethodVisitor}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - * @param mv - * the method visitor to which this visitor must delegate method - * calls. May be null. - */ - public MethodVisitor(final int api, final MethodVisitor mv) { - if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { - throw new IllegalArgumentException(); - } - this.api = api; - this.mv = mv; - } - - // ------------------------------------------------------------------------- - // Parameters, annotations and non standard attributes - // ------------------------------------------------------------------------- - - /** - * Visits a parameter of this method. - * - * @param name - * parameter name or null if none is provided. - * @param access - * the parameter's access flags, only ACC_FINAL, - * ACC_SYNTHETIC or/and ACC_MANDATED are - * allowed (see {@link Opcodes}). - */ - public void visitParameter(String name, int access) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } - if (mv != null) { - mv.visitParameter(name, access); - } - } - - /** - * Visits the default value of this annotation interface method. - * - * @return a visitor to the visit the actual default value of this - * annotation interface method, or null if this visitor is - * not interested in visiting this default value. The 'name' - * parameters passed to the methods of this annotation visitor are - * ignored. Moreover, exacly one visit method must be called on this - * annotation visitor, followed by visitEnd. - */ - public AnnotationVisitor visitAnnotationDefault() { - if (mv != null) { - return mv.visitAnnotationDefault(); - } - return null; - } - - /** - * Visits an annotation of this method. - * - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (mv != null) { - return mv.visitAnnotation(desc, visible); - } - return null; - } - - /** - * Visits an annotation on a type in the method signature. - * - * @param typeRef - * a reference to the annotated type. The sort of this type - * reference must be {@link TypeReference#METHOD_TYPE_PARAMETER - * METHOD_TYPE_PARAMETER}, - * {@link TypeReference#METHOD_TYPE_PARAMETER_BOUND - * METHOD_TYPE_PARAMETER_BOUND}, - * {@link TypeReference#METHOD_RETURN METHOD_RETURN}, - * {@link TypeReference#METHOD_RECEIVER METHOD_RECEIVER}, - * {@link TypeReference#METHOD_FORMAL_PARAMETER - * METHOD_FORMAL_PARAMETER} or {@link TypeReference#THROWS - * THROWS}. See {@link TypeReference}. - * @param typePath - * the path to the annotated type argument, wildcard bound, array - * element type, or static inner type within 'typeRef'. May be - * null if the annotation targets 'typeRef' as a whole. - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitTypeAnnotation(int typeRef, - TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } - if (mv != null) { - return mv.visitTypeAnnotation(typeRef, typePath, desc, visible); - } - return null; - } - - /** - * Visits an annotation of a parameter this method. - * - * @param parameter - * the parameter index. - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitParameterAnnotation(int parameter, - String desc, boolean visible) { - if (mv != null) { - return mv.visitParameterAnnotation(parameter, desc, visible); - } - return null; - } - - /** - * Visits a non standard attribute of this method. - * - * @param attr - * an attribute. - */ - public void visitAttribute(Attribute attr) { - if (mv != null) { - mv.visitAttribute(attr); - } - } - - /** - * Starts the visit of the method's code, if any (i.e. non abstract method). - */ - public void visitCode() { - if (mv != null) { - mv.visitCode(); - } - } - - /** - * Visits the current state of the local variables and operand stack - * elements. This method must(*) be called just before any - * instruction i that follows an unconditional branch instruction - * such as GOTO or THROW, that is the target of a jump instruction, or that - * starts an exception handler block. The visited types must describe the - * values of the local variables and of the operand stack elements just - * before i is executed.
- *
- * (*) this is mandatory only for classes whose version is greater than or - * equal to {@link Opcodes#V1_6 V1_6}.
- *
- * The frames of a method must be given either in expanded form, or in - * compressed form (all frames must use the same format, i.e. you must not - * mix expanded and compressed frames within a single method): - *
    - *
  • In expanded form, all frames must have the F_NEW type.
  • - *
  • In compressed form, frames are basically "deltas" from the state of - * the previous frame: - *
      - *
    • {@link Opcodes#F_SAME} representing frame with exactly the same - * locals as the previous frame and with the empty stack.
    • - *
    • {@link Opcodes#F_SAME1} representing frame with exactly the same - * locals as the previous frame and with single value on the stack ( - * nStack is 1 and stack[0] contains value for the - * type of the stack item).
    • - *
    • {@link Opcodes#F_APPEND} representing frame with current locals are - * the same as the locals in the previous frame, except that additional - * locals are defined (nLocal is 1, 2 or 3 and - * local elements contains values representing added types).
    • - *
    • {@link Opcodes#F_CHOP} representing frame with current locals are the - * same as the locals in the previous frame, except that the last 1-3 locals - * are absent and with the empty stack (nLocals is 1, 2 or 3).
    • - *
    • {@link Opcodes#F_FULL} representing complete frame data.
    • - *
    - *
  • - *
- *
- * In both cases the first frame, corresponding to the method's parameters - * and access flags, is implicit and must not be visited. Also, it is - * illegal to visit two or more frames for the same code location (i.e., at - * least one instruction must be visited between two calls to visitFrame). - * - * @param type - * the type of this stack map frame. Must be - * {@link Opcodes#F_NEW} for expanded frames, or - * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, - * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or - * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for - * compressed frames. - * @param nLocal - * the number of local variables in the visited frame. - * @param local - * the local variable types in this frame. This array must not be - * modified. Primitive types are represented by - * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, - * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, - * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or - * {@link Opcodes#UNINITIALIZED_THIS} (long and double are - * represented by a single element). Reference types are - * represented by String objects (representing internal names), - * and uninitialized types by Label objects (this label - * designates the NEW instruction that created this uninitialized - * value). - * @param nStack - * the number of operand stack elements in the visited frame. - * @param stack - * the operand stack types in this frame. This array must not be - * modified. Its content has the same format as the "local" - * array. - * @throws IllegalStateException - * if a frame is visited just after another one, without any - * instruction between the two (unless this frame is a - * Opcodes#F_SAME frame, in which case it is silently ignored). - */ - public void visitFrame(int type, int nLocal, Object[] local, int nStack, - Object[] stack) { - if (mv != null) { - mv.visitFrame(type, nLocal, local, nStack, stack); - } - } - - // ------------------------------------------------------------------------- - // Normal instructions - // ------------------------------------------------------------------------- - - /** - * Visits a zero operand instruction. - * - * @param opcode - * the opcode of the instruction to be visited. This opcode is - * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, - * ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, - * FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, - * LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, - * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, - * SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, - * DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, - * IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, - * FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, - * IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, - * L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, - * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, - * DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, - * or MONITOREXIT. - */ - public void visitInsn(int opcode) { - if (mv != null) { - mv.visitInsn(opcode); - } - } - - /** - * Visits an instruction with a single int operand. - * - * @param opcode - * the opcode of the instruction to be visited. This opcode is - * either BIPUSH, SIPUSH or NEWARRAY. - * @param operand - * the operand of the instruction to be visited.
- * When opcode is BIPUSH, operand value should be between - * Byte.MIN_VALUE and Byte.MAX_VALUE.
- * When opcode is SIPUSH, operand value should be between - * Short.MIN_VALUE and Short.MAX_VALUE.
- * When opcode is NEWARRAY, operand value should be one of - * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR}, - * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, - * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT}, - * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. - */ - public void visitIntInsn(int opcode, int operand) { - if (mv != null) { - mv.visitIntInsn(opcode, operand); - } - } - - /** - * Visits a local variable instruction. A local variable instruction is an - * instruction that loads or stores the value of a local variable. - * - * @param opcode - * the opcode of the local variable instruction to be visited. - * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, - * ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. - * @param var - * the operand of the instruction to be visited. This operand is - * the index of a local variable. - */ - public void visitVarInsn(int opcode, int var) { - if (mv != null) { - mv.visitVarInsn(opcode, var); - } - } - - /** - * Visits a type instruction. A type instruction is an instruction that - * takes the internal name of a class as parameter. - * - * @param opcode - * the opcode of the type instruction to be visited. This opcode - * is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. - * @param type - * the operand of the instruction to be visited. This operand - * must be the internal name of an object or array class (see - * {@link Type#getInternalName() getInternalName}). - */ - public void visitTypeInsn(int opcode, String type) { - if (mv != null) { - mv.visitTypeInsn(opcode, type); - } - } - - /** - * Visits a field instruction. A field instruction is an instruction that - * loads or stores the value of a field of an object. - * - * @param opcode - * the opcode of the type instruction to be visited. This opcode - * is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. - * @param owner - * the internal name of the field's owner class (see - * {@link Type#getInternalName() getInternalName}). - * @param name - * the field's name. - * @param desc - * the field's descriptor (see {@link Type Type}). - */ - public void visitFieldInsn(int opcode, String owner, String name, - String desc) { - if (mv != null) { - mv.visitFieldInsn(opcode, owner, name, desc); - } - } - - /** - * Visits a method instruction. A method instruction is an instruction that - * invokes a method. - * - * @param opcode - * the opcode of the type instruction to be visited. This opcode - * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or - * INVOKEINTERFACE. - * @param owner - * the internal name of the method's owner class (see - * {@link Type#getInternalName() getInternalName}). - * @param name - * the method's name. - * @param desc - * the method's descriptor (see {@link Type Type}). - */ - @Deprecated - public void visitMethodInsn(int opcode, String owner, String name, - String desc) { - if (api >= Opcodes.ASM5) { - boolean itf = opcode == Opcodes.INVOKEINTERFACE; - visitMethodInsn(opcode, owner, name, desc, itf); - return; - } - if (mv != null) { - mv.visitMethodInsn(opcode, owner, name, desc); - } - } - - /** - * Visits a method instruction. A method instruction is an instruction that - * invokes a method. - * - * @param opcode - * the opcode of the type instruction to be visited. This opcode - * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or - * INVOKEINTERFACE. - * @param owner - * the internal name of the method's owner class (see - * {@link Type#getInternalName() getInternalName}). - * @param name - * the method's name. - * @param desc - * the method's descriptor (see {@link Type Type}). - * @param itf - * if the method's owner class is an interface. - */ - public void visitMethodInsn(int opcode, String owner, String name, - String desc, boolean itf) { - if (api < Opcodes.ASM5) { - if (itf != (opcode == Opcodes.INVOKEINTERFACE)) { - throw new IllegalArgumentException( - "INVOKESPECIAL/STATIC on interfaces require ASM 5"); - } - visitMethodInsn(opcode, owner, name, desc); - return; - } - if (mv != null) { - mv.visitMethodInsn(opcode, owner, name, desc, itf); - } - } - - /** - * Visits an invokedynamic instruction. - * - * @param name - * the method's name. - * @param desc - * the method's descriptor (see {@link Type Type}). - * @param bsm - * the bootstrap method. - * @param bsmArgs - * the bootstrap method constant arguments. Each argument must be - * an {@link Integer}, {@link Float}, {@link Long}, - * {@link Double}, {@link String}, {@link Type} or {@link Handle} - * value. This method is allowed to modify the content of the - * array so a caller should expect that this array may change. - */ - public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, - Object... bsmArgs) { - if (mv != null) { - mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); - } - } - - /** - * Visits a jump instruction. A jump instruction is an instruction that may - * jump to another instruction. - * - * @param opcode - * the opcode of the type instruction to be visited. This opcode - * is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, - * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, - * IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. - * @param label - * the operand of the instruction to be visited. This operand is - * a label that designates the instruction to which the jump - * instruction may jump. - */ - public void visitJumpInsn(int opcode, Label label) { - if (mv != null) { - mv.visitJumpInsn(opcode, label); - } - } - - /** - * Visits a label. A label designates the instruction that will be visited - * just after it. - * - * @param label - * a {@link Label Label} object. - */ - public void visitLabel(Label label) { - if (mv != null) { - mv.visitLabel(label); - } - } - - // ------------------------------------------------------------------------- - // Special instructions - // ------------------------------------------------------------------------- - - /** - * Visits a LDC instruction. Note that new constant types may be added in - * future versions of the Java Virtual Machine. To easily detect new - * constant types, implementations of this method should check for - * unexpected constant types, like this: - * - *
-     * if (cst instanceof Integer) {
-     *     // ...
-     * } else if (cst instanceof Float) {
-     *     // ...
-     * } else if (cst instanceof Long) {
-     *     // ...
-     * } else if (cst instanceof Double) {
-     *     // ...
-     * } else if (cst instanceof String) {
-     *     // ...
-     * } else if (cst instanceof Type) {
-     *     int sort = ((Type) cst).getSort();
-     *     if (sort == Type.OBJECT) {
-     *         // ...
-     *     } else if (sort == Type.ARRAY) {
-     *         // ...
-     *     } else if (sort == Type.METHOD) {
-     *         // ...
-     *     } else {
-     *         // throw an exception
-     *     }
-     * } else if (cst instanceof Handle) {
-     *     // ...
-     * } else {
-     *     // throw an exception
-     * }
-     * 
- * - * @param cst - * the constant to be loaded on the stack. This parameter must be - * a non null {@link Integer}, a {@link Float}, a {@link Long}, a - * {@link Double}, a {@link String}, a {@link Type} of OBJECT or - * ARRAY sort for .class constants, for classes whose - * version is 49.0, a {@link Type} of METHOD sort or a - * {@link Handle} for MethodType and MethodHandle constants, for - * classes whose version is 51.0. - */ - public void visitLdcInsn(Object cst) { - if (mv != null) { - mv.visitLdcInsn(cst); - } - } - - /** - * Visits an IINC instruction. - * - * @param var - * index of the local variable to be incremented. - * @param increment - * amount to increment the local variable by. - */ - public void visitIincInsn(int var, int increment) { - if (mv != null) { - mv.visitIincInsn(var, increment); - } - } - - /** - * Visits a TABLESWITCH instruction. - * - * @param min - * the minimum key value. - * @param max - * the maximum key value. - * @param dflt - * beginning of the default handler block. - * @param labels - * beginnings of the handler blocks. labels[i] is the - * beginning of the handler block for the min + i key. - */ - public void visitTableSwitchInsn(int min, int max, Label dflt, - Label... labels) { - if (mv != null) { - mv.visitTableSwitchInsn(min, max, dflt, labels); - } - } - - /** - * Visits a LOOKUPSWITCH instruction. - * - * @param dflt - * beginning of the default handler block. - * @param keys - * the values of the keys. - * @param labels - * beginnings of the handler blocks. labels[i] is the - * beginning of the handler block for the keys[i] key. - */ - public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { - if (mv != null) { - mv.visitLookupSwitchInsn(dflt, keys, labels); - } - } - - /** - * Visits a MULTIANEWARRAY instruction. - * - * @param desc - * an array type descriptor (see {@link Type Type}). - * @param dims - * number of dimensions of the array to allocate. - */ - public void visitMultiANewArrayInsn(String desc, int dims) { - if (mv != null) { - mv.visitMultiANewArrayInsn(desc, dims); - } - } - - /** - * Visits an annotation on an instruction. This method must be called just - * after the annotated instruction. It can be called several times - * for the same instruction. - * - * @param typeRef - * a reference to the annotated type. The sort of this type - * reference must be {@link TypeReference#INSTANCEOF INSTANCEOF}, - * {@link TypeReference#NEW NEW}, - * {@link TypeReference#CONSTRUCTOR_REFERENCE - * CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE - * METHOD_REFERENCE}, {@link TypeReference#CAST CAST}, - * {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT - * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, - * {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT - * METHOD_INVOCATION_TYPE_ARGUMENT}, - * {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT - * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or - * {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT - * METHOD_REFERENCE_TYPE_ARGUMENT}. See {@link TypeReference}. - * @param typePath - * the path to the annotated type argument, wildcard bound, array - * element type, or static inner type within 'typeRef'. May be - * null if the annotation targets 'typeRef' as a whole. - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitInsnAnnotation(int typeRef, - TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } - if (mv != null) { - return mv.visitInsnAnnotation(typeRef, typePath, desc, visible); - } - return null; - } - - // ------------------------------------------------------------------------- - // Exceptions table entries, debug information, max stack and max locals - // ------------------------------------------------------------------------- - - /** - * Visits a try catch block. - * - * @param start - * beginning of the exception handler's scope (inclusive). - * @param end - * end of the exception handler's scope (exclusive). - * @param handler - * beginning of the exception handler's code. - * @param type - * internal name of the type of exceptions handled by the - * handler, or null to catch any exceptions (for - * "finally" blocks). - * @throws IllegalArgumentException - * if one of the labels has already been visited by this visitor - * (by the {@link #visitLabel visitLabel} method). - */ - public void visitTryCatchBlock(Label start, Label end, Label handler, - String type) { - if (mv != null) { - mv.visitTryCatchBlock(start, end, handler, type); - } - } - - /** - * Visits an annotation on an exception handler type. This method must be - * called after the {@link #visitTryCatchBlock} for the annotated - * exception handler. It can be called several times for the same exception - * handler. - * - * @param typeRef - * a reference to the annotated type. The sort of this type - * reference must be {@link TypeReference#EXCEPTION_PARAMETER - * EXCEPTION_PARAMETER}. See {@link TypeReference}. - * @param typePath - * the path to the annotated type argument, wildcard bound, array - * element type, or static inner type within 'typeRef'. May be - * null if the annotation targets 'typeRef' as a whole. - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitTryCatchAnnotation(int typeRef, - TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } - if (mv != null) { - return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible); - } - return null; - } - - /** - * Visits a local variable declaration. - * - * @param name - * the name of a local variable. - * @param desc - * the type descriptor of this local variable. - * @param signature - * the type signature of this local variable. May be - * null if the local variable type does not use generic - * types. - * @param start - * the first instruction corresponding to the scope of this local - * variable (inclusive). - * @param end - * the last instruction corresponding to the scope of this local - * variable (exclusive). - * @param index - * the local variable's index. - * @throws IllegalArgumentException - * if one of the labels has not already been visited by this - * visitor (by the {@link #visitLabel visitLabel} method). - */ - public void visitLocalVariable(String name, String desc, String signature, - Label start, Label end, int index) { - if (mv != null) { - mv.visitLocalVariable(name, desc, signature, start, end, index); - } - } - - /** - * Visits an annotation on a local variable type. - * - * @param typeRef - * a reference to the annotated type. The sort of this type - * reference must be {@link TypeReference#LOCAL_VARIABLE - * LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE - * RESOURCE_VARIABLE}. See {@link TypeReference}. - * @param typePath - * the path to the annotated type argument, wildcard bound, array - * element type, or static inner type within 'typeRef'. May be - * null if the annotation targets 'typeRef' as a whole. - * @param start - * the fist instructions corresponding to the continuous ranges - * that make the scope of this local variable (inclusive). - * @param end - * the last instructions corresponding to the continuous ranges - * that make the scope of this local variable (exclusive). This - * array must have the same size as the 'start' array. - * @param index - * the local variable's index in each range. This array must have - * the same size as the 'start' array. - * @param desc - * the class descriptor of the annotation class. - * @param visible - * true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ - public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, - TypePath typePath, Label[] start, Label[] end, int[] index, - String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } - if (mv != null) { - return mv.visitLocalVariableAnnotation(typeRef, typePath, start, - end, index, desc, visible); - } - return null; - } - - /** - * Visits a line number declaration. - * - * @param line - * a line number. This number refers to the source file from - * which the class was compiled. - * @param start - * the first instruction corresponding to this line number. - * @throws IllegalArgumentException - * if start has not already been visited by this - * visitor (by the {@link #visitLabel visitLabel} method). - */ - public void visitLineNumber(int line, Label start) { - if (mv != null) { - mv.visitLineNumber(line, start); - } - } - - /** - * Visits the maximum stack size and the maximum number of local variables - * of the method. - * - * @param maxStack - * maximum stack size of the method. - * @param maxLocals - * maximum number of local variables for the method. - */ - public void visitMaxs(int maxStack, int maxLocals) { - if (mv != null) { - mv.visitMaxs(maxStack, maxLocals); - } - } - - /** - * Visits the end of the method. This method, which is the last one to be - * called, is used to inform the visitor that all the annotations and - * attributes of the method have been visited. - */ - public void visitEnd() { - if (mv != null) { - mv.visitEnd(); - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/MethodWriter.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/MethodWriter.java deleted file mode 100644 index e3111a02..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/MethodWriter.java +++ /dev/null @@ -1,2915 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A {@link MethodVisitor} that generates methods in bytecode form. Each visit - * method of this class appends the bytecode corresponding to the visited - * instruction to a byte vector, in the order these methods are called. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -class MethodWriter extends MethodVisitor { - - /** - * Pseudo access flag used to denote constructors. - */ - static final int ACC_CONSTRUCTOR = 0x80000; - - /** - * Frame has exactly the same locals as the previous stack map frame and - * number of stack items is zero. - */ - static final int SAME_FRAME = 0; // to 63 (0-3f) - - /** - * Frame has exactly the same locals as the previous stack map frame and - * number of stack items is 1 - */ - static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) - - /** - * Reserved for future use - */ - static final int RESERVED = 128; - - /** - * Frame has exactly the same locals as the previous stack map frame and - * number of stack items is 1. Offset is bigger then 63; - */ - static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 - - /** - * Frame where current locals are the same as the locals in the previous - * frame, except that the k last locals are absent. The value of k is given - * by the formula 251-frame_type. - */ - static final int CHOP_FRAME = 248; // to 250 (f8-fA) - - /** - * Frame has exactly the same locals as the previous stack map frame and - * number of stack items is zero. Offset is bigger then 63; - */ - static final int SAME_FRAME_EXTENDED = 251; // fb - - /** - * Frame where current locals are the same as the locals in the previous - * frame, except that k additional locals are defined. The value of k is - * given by the formula frame_type-251. - */ - static final int APPEND_FRAME = 252; // to 254 // fc-fe - - /** - * Full frame - */ - static final int FULL_FRAME = 255; // ff - - /** - * Indicates that the stack map frames must be recomputed from scratch. In - * this case the maximum stack size and number of local variables is also - * recomputed from scratch. - * - * @see #compute - */ - private static final int FRAMES = 0; - - /** - * Indicates that the maximum stack size and number of local variables must - * be automatically computed. - * - * @see #compute - */ - private static final int MAXS = 1; - - /** - * Indicates that nothing must be automatically computed. - * - * @see #compute - */ - private static final int NOTHING = 2; - - /** - * The class writer to which this method must be added. - */ - final ClassWriter cw; - - /** - * Access flags of this method. - */ - private int access; - - /** - * The index of the constant pool item that contains the name of this - * method. - */ - private final int name; - - /** - * The index of the constant pool item that contains the descriptor of this - * method. - */ - private final int desc; - - /** - * The descriptor of this method. - */ - private final String descriptor; - - /** - * The signature of this method. - */ - String signature; - - /** - * If not zero, indicates that the code of this method must be copied from - * the ClassReader associated to this writer in cw.cr. More - * precisely, this field gives the index of the first byte to copied from - * cw.cr.b. - */ - int classReaderOffset; - - /** - * If not zero, indicates that the code of this method must be copied from - * the ClassReader associated to this writer in cw.cr. More - * precisely, this field gives the number of bytes to copied from - * cw.cr.b. - */ - int classReaderLength; - - /** - * Number of exceptions that can be thrown by this method. - */ - int exceptionCount; - - /** - * The exceptions that can be thrown by this method. More precisely, this - * array contains the indexes of the constant pool items that contain the - * internal names of these exception classes. - */ - int[] exceptions; - - /** - * The annotation default attribute of this method. May be null. - */ - private ByteVector annd; - - /** - * The runtime visible annotations of this method. May be null. - */ - private AnnotationWriter anns; - - /** - * The runtime invisible annotations of this method. May be null. - */ - private AnnotationWriter ianns; - - /** - * The runtime visible type annotations of this method. May be null - * . - */ - private AnnotationWriter tanns; - - /** - * The runtime invisible type annotations of this method. May be - * null. - */ - private AnnotationWriter itanns; - - /** - * The runtime visible parameter annotations of this method. May be - * null. - */ - private AnnotationWriter[] panns; - - /** - * The runtime invisible parameter annotations of this method. May be - * null. - */ - private AnnotationWriter[] ipanns; - - /** - * The number of synthetic parameters of this method. - */ - private int synthetics; - - /** - * The non standard attributes of the method. - */ - private Attribute attrs; - - /** - * The bytecode of this method. - */ - private ByteVector code = new ByteVector(); - - /** - * Maximum stack size of this method. - */ - private int maxStack; - - /** - * Maximum number of local variables for this method. - */ - private int maxLocals; - - /** - * Number of local variables in the current stack map frame. - */ - private int currentLocals; - - /** - * Number of stack map frames in the StackMapTable attribute. - */ - private int frameCount; - - /** - * The StackMapTable attribute. - */ - private ByteVector stackMap; - - /** - * The offset of the last frame that was written in the StackMapTable - * attribute. - */ - private int previousFrameOffset; - - /** - * The last frame that was written in the StackMapTable attribute. - * - * @see #frame - */ - private int[] previousFrame; - - /** - * The current stack map frame. The first element contains the offset of the - * instruction to which the frame corresponds, the second element is the - * number of locals and the third one is the number of stack elements. The - * local variables start at index 3 and are followed by the operand stack - * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = - * nStack, frame[3] = nLocal. All types are encoded as integers, with the - * same format as the one used in {@link Label}, but limited to BASE types. - */ - private int[] frame; - - /** - * Number of elements in the exception handler list. - */ - private int handlerCount; - - /** - * The first element in the exception handler list. - */ - private Handler firstHandler; - - /** - * The last element in the exception handler list. - */ - private Handler lastHandler; - - /** - * Number of entries in the MethodParameters attribute. - */ - private int methodParametersCount; - - /** - * The MethodParameters attribute. - */ - private ByteVector methodParameters; - - /** - * Number of entries in the LocalVariableTable attribute. - */ - private int localVarCount; - - /** - * The LocalVariableTable attribute. - */ - private ByteVector localVar; - - /** - * Number of entries in the LocalVariableTypeTable attribute. - */ - private int localVarTypeCount; - - /** - * The LocalVariableTypeTable attribute. - */ - private ByteVector localVarType; - - /** - * Number of entries in the LineNumberTable attribute. - */ - private int lineNumberCount; - - /** - * The LineNumberTable attribute. - */ - private ByteVector lineNumber; - - /** - * The start offset of the last visited instruction. - */ - private int lastCodeOffset; - - /** - * The runtime visible type annotations of the code. May be null. - */ - private AnnotationWriter ctanns; - - /** - * The runtime invisible type annotations of the code. May be null. - */ - private AnnotationWriter ictanns; - - /** - * The non standard attributes of the method's code. - */ - private Attribute cattrs; - - /** - * Indicates if some jump instructions are too small and need to be resized. - */ - private boolean resize; - - /** - * The number of subroutines in this method. - */ - private int subroutines; - - // ------------------------------------------------------------------------ - - /* - * Fields for the control flow graph analysis algorithm (used to compute the - * maximum stack size). A control flow graph contains one node per "basic - * block", and one edge per "jump" from one basic block to another. Each - * node (i.e., each basic block) is represented by the Label object that - * corresponds to the first instruction of this basic block. Each node also - * stores the list of its successors in the graph, as a linked list of Edge - * objects. - */ - - /** - * Indicates what must be automatically computed. - * - * @see #FRAMES - * @see #MAXS - * @see #NOTHING - */ - private final int compute; - - /** - * A list of labels. This list is the list of basic blocks in the method, - * i.e. a list of Label objects linked to each other by their - * {@link Label#successor} field, in the order they are visited by - * {@link MethodVisitor#visitLabel}, and starting with the first basic - * block. - */ - private Label labels; - - /** - * The previous basic block. - */ - private Label previousBlock; - - /** - * The current basic block. - */ - private Label currentBlock; - - /** - * The (relative) stack size after the last visited instruction. This size - * is relative to the beginning of the current basic block, i.e., the true - * stack size after the last visited instruction is equal to the - * {@link Label#inputStackTop beginStackSize} of the current basic block - * plus stackSize. - */ - private int stackSize; - - /** - * The (relative) maximum stack size after the last visited instruction. - * This size is relative to the beginning of the current basic block, i.e., - * the true maximum stack size after the last visited instruction is equal - * to the {@link Label#inputStackTop beginStackSize} of the current basic - * block plus stackSize. - */ - private int maxStackSize; - - // ------------------------------------------------------------------------ - // Constructor - // ------------------------------------------------------------------------ - - /** - * Constructs a new {@link MethodWriter}. - * - * @param cw - * the class writer in which the method must be added. - * @param access - * the method's access flags (see {@link Opcodes}). - * @param name - * the method's name. - * @param desc - * the method's descriptor (see {@link Type}). - * @param signature - * the method's signature. May be null. - * @param exceptions - * the internal names of the method's exceptions. May be - * null. - * @param computeMaxs - * true if the maximum stack size and number of local - * variables must be automatically computed. - * @param computeFrames - * true if the stack map tables must be recomputed from - * scratch. - */ - MethodWriter(final ClassWriter cw, final int access, final String name, - final String desc, final String signature, - final String[] exceptions, final boolean computeMaxs, - final boolean computeFrames) { - super(Opcodes.ASM5); - if (cw.firstMethod == null) { - cw.firstMethod = this; - } else { - cw.lastMethod.mv = this; - } - cw.lastMethod = this; - this.cw = cw; - this.access = access; - if ("".equals(name)) { - this.access |= ACC_CONSTRUCTOR; - } - this.name = cw.newUTF8(name); - this.desc = cw.newUTF8(desc); - this.descriptor = desc; - if (ClassReader.SIGNATURES) { - this.signature = signature; - } - if (exceptions != null && exceptions.length > 0) { - exceptionCount = exceptions.length; - this.exceptions = new int[exceptionCount]; - for (int i = 0; i < exceptionCount; ++i) { - this.exceptions[i] = cw.newClass(exceptions[i]); - } - } - this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); - if (computeMaxs || computeFrames) { - // updates maxLocals - int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2; - if ((access & Opcodes.ACC_STATIC) != 0) { - --size; - } - maxLocals = size; - currentLocals = size; - // creates and visits the label for the first basic block - labels = new Label(); - labels.status |= Label.PUSHED; - visitLabel(labels); - } - } - - // ------------------------------------------------------------------------ - // Implementation of the MethodVisitor abstract class - // ------------------------------------------------------------------------ - - @Override - public void visitParameter(String name, int access) { - if (methodParameters == null) { - methodParameters = new ByteVector(); - } - ++methodParametersCount; - methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name)) - .putShort(access); - } - - @Override - public AnnotationVisitor visitAnnotationDefault() { - if (!ClassReader.ANNOTATIONS) { - return null; - } - annd = new ByteVector(); - return new AnnotationWriter(cw, false, annd, null, 0); - } - - @Override - public AnnotationVisitor visitAnnotation(final String desc, - final boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); - if (visible) { - aw.next = anns; - anns = aw; - } else { - aw.next = ianns; - ianns = aw; - } - return aw; - } - - @Override - public AnnotationVisitor visitTypeAnnotation(final int typeRef, - final TypePath typePath, final String desc, final boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write target_type and target_info - AnnotationWriter.putTarget(typeRef, typePath, bv); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, - bv.length - 2); - if (visible) { - aw.next = tanns; - tanns = aw; - } else { - aw.next = itanns; - itanns = aw; - } - return aw; - } - - @Override - public AnnotationVisitor visitParameterAnnotation(final int parameter, - final String desc, final boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - if ("Ljava/lang/Synthetic;".equals(desc)) { - // workaround for a bug in javac with synthetic parameters - // see ClassReader.readParameterAnnotations - synthetics = Math.max(synthetics, parameter + 1); - return new AnnotationWriter(cw, false, bv, null, 0); - } - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); - if (visible) { - if (panns == null) { - panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; - } - aw.next = panns[parameter]; - panns[parameter] = aw; - } else { - if (ipanns == null) { - ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; - } - aw.next = ipanns[parameter]; - ipanns[parameter] = aw; - } - return aw; - } - - @Override - public void visitAttribute(final Attribute attr) { - if (attr.isCodeAttribute()) { - attr.next = cattrs; - cattrs = attr; - } else { - attr.next = attrs; - attrs = attr; - } - } - - @Override - public void visitCode() { - } - - @Override - public void visitFrame(final int type, final int nLocal, - final Object[] local, final int nStack, final Object[] stack) { - if (!ClassReader.FRAMES || compute == FRAMES) { - return; - } - - if (type == Opcodes.F_NEW) { - if (previousFrame == null) { - visitImplicitFirstFrame(); - } - currentLocals = nLocal; - int frameIndex = startFrame(code.length, nLocal, nStack); - for (int i = 0; i < nLocal; ++i) { - if (local[i] instanceof String) { - frame[frameIndex++] = Frame.OBJECT - | cw.addType((String) local[i]); - } else if (local[i] instanceof Integer) { - frame[frameIndex++] = ((Integer) local[i]).intValue(); - } else { - frame[frameIndex++] = Frame.UNINITIALIZED - | cw.addUninitializedType("", - ((Label) local[i]).position); - } - } - for (int i = 0; i < nStack; ++i) { - if (stack[i] instanceof String) { - frame[frameIndex++] = Frame.OBJECT - | cw.addType((String) stack[i]); - } else if (stack[i] instanceof Integer) { - frame[frameIndex++] = ((Integer) stack[i]).intValue(); - } else { - frame[frameIndex++] = Frame.UNINITIALIZED - | cw.addUninitializedType("", - ((Label) stack[i]).position); - } - } - endFrame(); - } else { - int delta; - if (stackMap == null) { - stackMap = new ByteVector(); - delta = code.length; - } else { - delta = code.length - previousFrameOffset - 1; - if (delta < 0) { - if (type == Opcodes.F_SAME) { - return; - } else { - throw new IllegalStateException(); - } - } - } - - switch (type) { - case Opcodes.F_FULL: - currentLocals = nLocal; - stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal); - for (int i = 0; i < nLocal; ++i) { - writeFrameType(local[i]); - } - stackMap.putShort(nStack); - for (int i = 0; i < nStack; ++i) { - writeFrameType(stack[i]); - } - break; - case Opcodes.F_APPEND: - currentLocals += nLocal; - stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta); - for (int i = 0; i < nLocal; ++i) { - writeFrameType(local[i]); - } - break; - case Opcodes.F_CHOP: - currentLocals -= nLocal; - stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta); - break; - case Opcodes.F_SAME: - if (delta < 64) { - stackMap.putByte(delta); - } else { - stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); - } - break; - case Opcodes.F_SAME1: - if (delta < 64) { - stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); - } else { - stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) - .putShort(delta); - } - writeFrameType(stack[0]); - break; - } - - previousFrameOffset = code.length; - ++frameCount; - } - - maxStack = Math.max(maxStack, nStack); - maxLocals = Math.max(maxLocals, currentLocals); - } - - @Override - public void visitInsn(final int opcode) { - lastCodeOffset = code.length; - // adds the instruction to the bytecode of the method - code.putByte(opcode); - // update currentBlock - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(opcode, 0, null, null); - } else { - // updates current and max stack sizes - int size = stackSize + Frame.SIZE[opcode]; - if (size > maxStackSize) { - maxStackSize = size; - } - stackSize = size; - } - // if opcode == ATHROW or xRETURN, ends current block (no successor) - if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) - || opcode == Opcodes.ATHROW) { - noSuccessor(); - } - } - } - - @Override - public void visitIntInsn(final int opcode, final int operand) { - lastCodeOffset = code.length; - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(opcode, operand, null, null); - } else if (opcode != Opcodes.NEWARRAY) { - // updates current and max stack sizes only for NEWARRAY - // (stack size variation = 0 for BIPUSH or SIPUSH) - int size = stackSize + 1; - if (size > maxStackSize) { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - if (opcode == Opcodes.SIPUSH) { - code.put12(opcode, operand); - } else { // BIPUSH or NEWARRAY - code.put11(opcode, operand); - } - } - - @Override - public void visitVarInsn(final int opcode, final int var) { - lastCodeOffset = code.length; - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(opcode, var, null, null); - } else { - // updates current and max stack sizes - if (opcode == Opcodes.RET) { - // no stack change, but end of current block (no successor) - currentBlock.status |= Label.RET; - // save 'stackSize' here for future use - // (see {@link #findSubroutineSuccessors}) - currentBlock.inputStackTop = stackSize; - noSuccessor(); - } else { // xLOAD or xSTORE - int size = stackSize + Frame.SIZE[opcode]; - if (size > maxStackSize) { - maxStackSize = size; - } - stackSize = size; - } - } - } - if (compute != NOTHING) { - // updates max locals - int n; - if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD - || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) { - n = var + 2; - } else { - n = var + 1; - } - if (n > maxLocals) { - maxLocals = n; - } - } - // adds the instruction to the bytecode of the method - if (var < 4 && opcode != Opcodes.RET) { - int opt; - if (opcode < Opcodes.ISTORE) { - /* ILOAD_0 */ - opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; - } else { - /* ISTORE_0 */ - opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; - } - code.putByte(opt); - } else if (var >= 256) { - code.putByte(196 /* WIDE */).put12(opcode, var); - } else { - code.put11(opcode, var); - } - if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) { - visitLabel(new Label()); - } - } - - @Override - public void visitTypeInsn(final int opcode, final String type) { - lastCodeOffset = code.length; - Item i = cw.newClassItem(type); - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(opcode, code.length, cw, i); - } else if (opcode == Opcodes.NEW) { - // updates current and max stack sizes only if opcode == NEW - // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) - int size = stackSize + 1; - if (size > maxStackSize) { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - code.put12(opcode, i.index); - } - - @Override - public void visitFieldInsn(final int opcode, final String owner, - final String name, final String desc) { - lastCodeOffset = code.length; - Item i = cw.newFieldItem(owner, name, desc); - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(opcode, 0, cw, i); - } else { - int size; - // computes the stack size variation - char c = desc.charAt(0); - switch (opcode) { - case Opcodes.GETSTATIC: - size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); - break; - case Opcodes.PUTSTATIC: - size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); - break; - case Opcodes.GETFIELD: - size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); - break; - // case Constants.PUTFIELD: - default: - size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); - break; - } - // updates current and max stack sizes - if (size > maxStackSize) { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - code.put12(opcode, i.index); - } - - @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc, final boolean itf) { - lastCodeOffset = code.length; - Item i = cw.newMethodItem(owner, name, desc, itf); - int argSize = i.intVal; - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(opcode, 0, cw, i); - } else { - /* - * computes the stack size variation. In order not to recompute - * several times this variation for the same Item, we use the - * intVal field of this item to store this variation, once it - * has been computed. More precisely this intVal field stores - * the sizes of the arguments and of the return value - * corresponding to desc. - */ - if (argSize == 0) { - // the above sizes have not been computed yet, - // so we compute them... - argSize = Type.getArgumentsAndReturnSizes(desc); - // ... and we save them in order - // not to recompute them in the future - i.intVal = argSize; - } - int size; - if (opcode == Opcodes.INVOKESTATIC) { - size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; - } else { - size = stackSize - (argSize >> 2) + (argSize & 0x03); - } - // updates current and max stack sizes - if (size > maxStackSize) { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - if (opcode == Opcodes.INVOKEINTERFACE) { - if (argSize == 0) { - argSize = Type.getArgumentsAndReturnSizes(desc); - i.intVal = argSize; - } - code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); - } else { - code.put12(opcode, i.index); - } - } - - @Override - public void visitInvokeDynamicInsn(final String name, final String desc, - final Handle bsm, final Object... bsmArgs) { - lastCodeOffset = code.length; - Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs); - int argSize = i.intVal; - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i); - } else { - /* - * computes the stack size variation. In order not to recompute - * several times this variation for the same Item, we use the - * intVal field of this item to store this variation, once it - * has been computed. More precisely this intVal field stores - * the sizes of the arguments and of the return value - * corresponding to desc. - */ - if (argSize == 0) { - // the above sizes have not been computed yet, - // so we compute them... - argSize = Type.getArgumentsAndReturnSizes(desc); - // ... and we save them in order - // not to recompute them in the future - i.intVal = argSize; - } - int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; - - // updates current and max stack sizes - if (size > maxStackSize) { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - code.put12(Opcodes.INVOKEDYNAMIC, i.index); - code.putShort(0); - } - - @Override - public void visitJumpInsn(final int opcode, final Label label) { - lastCodeOffset = code.length; - Label nextInsn = null; - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(opcode, 0, null, null); - // 'label' is the target of a jump instruction - label.getFirst().status |= Label.TARGET; - // adds 'label' as a successor of this basic block - addSuccessor(Edge.NORMAL, label); - if (opcode != Opcodes.GOTO) { - // creates a Label for the next basic block - nextInsn = new Label(); - } - } else { - if (opcode == Opcodes.JSR) { - if ((label.status & Label.SUBROUTINE) == 0) { - label.status |= Label.SUBROUTINE; - ++subroutines; - } - currentBlock.status |= Label.JSR; - addSuccessor(stackSize + 1, label); - // creates a Label for the next basic block - nextInsn = new Label(); - /* - * note that, by construction in this method, a JSR block - * has at least two successors in the control flow graph: - * the first one leads the next instruction after the JSR, - * while the second one leads to the JSR target. - */ - } else { - // updates current stack size (max stack size unchanged - // because stack size variation always negative in this - // case) - stackSize += Frame.SIZE[opcode]; - addSuccessor(stackSize, label); - } - } - } - // adds the instruction to the bytecode of the method - if ((label.status & Label.RESOLVED) != 0 - && label.position - code.length < Short.MIN_VALUE) { - /* - * case of a backward jump with an offset < -32768. In this case we - * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx - * with IFNOTxxx GOTO_W , where IFNOTxxx is the - * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where - * designates the instruction just after the GOTO_W. - */ - if (opcode == Opcodes.GOTO) { - code.putByte(200); // GOTO_W - } else if (opcode == Opcodes.JSR) { - code.putByte(201); // JSR_W - } else { - // if the IF instruction is transformed into IFNOT GOTO_W the - // next instruction becomes the target of the IFNOT instruction - if (nextInsn != null) { - nextInsn.status |= Label.TARGET; - } - code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 - : opcode ^ 1); - code.putShort(8); // jump offset - code.putByte(200); // GOTO_W - } - label.put(this, code, code.length - 1, true); - } else { - /* - * case of a backward jump with an offset >= -32768, or of a forward - * jump with, of course, an unknown offset. In these cases we store - * the offset in 2 bytes (which will be increased in - * resizeInstructions, if needed). - */ - code.putByte(opcode); - label.put(this, code, code.length - 1, false); - } - if (currentBlock != null) { - if (nextInsn != null) { - // if the jump instruction is not a GOTO, the next instruction - // is also a successor of this instruction. Calling visitLabel - // adds the label of this next instruction as a successor of the - // current block, and starts a new basic block - visitLabel(nextInsn); - } - if (opcode == Opcodes.GOTO) { - noSuccessor(); - } - } - } - - @Override - public void visitLabel(final Label label) { - // resolves previous forward references to label, if any - resize |= label.resolve(this, code.length, code.data); - // updates currentBlock - if ((label.status & Label.DEBUG) != 0) { - return; - } - if (compute == FRAMES) { - if (currentBlock != null) { - if (label.position == currentBlock.position) { - // successive labels, do not start a new basic block - currentBlock.status |= (label.status & Label.TARGET); - label.frame = currentBlock.frame; - return; - } - // ends current block (with one new successor) - addSuccessor(Edge.NORMAL, label); - } - // begins a new current block - currentBlock = label; - if (label.frame == null) { - label.frame = new Frame(); - label.frame.owner = label; - } - // updates the basic block list - if (previousBlock != null) { - if (label.position == previousBlock.position) { - previousBlock.status |= (label.status & Label.TARGET); - label.frame = previousBlock.frame; - currentBlock = previousBlock; - return; - } - previousBlock.successor = label; - } - previousBlock = label; - } else if (compute == MAXS) { - if (currentBlock != null) { - // ends current block (with one new successor) - currentBlock.outputStackMax = maxStackSize; - addSuccessor(stackSize, label); - } - // begins a new current block - currentBlock = label; - // resets the relative current and max stack sizes - stackSize = 0; - maxStackSize = 0; - // updates the basic block list - if (previousBlock != null) { - previousBlock.successor = label; - } - previousBlock = label; - } - } - - @Override - public void visitLdcInsn(final Object cst) { - lastCodeOffset = code.length; - Item i = cw.newConstItem(cst); - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); - } else { - int size; - // computes the stack size variation - if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { - size = stackSize + 2; - } else { - size = stackSize + 1; - } - // updates current and max stack sizes - if (size > maxStackSize) { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - int index = i.index; - if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { - code.put12(20 /* LDC2_W */, index); - } else if (index >= 256) { - code.put12(19 /* LDC_W */, index); - } else { - code.put11(Opcodes.LDC, index); - } - } - - @Override - public void visitIincInsn(final int var, final int increment) { - lastCodeOffset = code.length; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(Opcodes.IINC, var, null, null); - } - } - if (compute != NOTHING) { - // updates max locals - int n = var + 1; - if (n > maxLocals) { - maxLocals = n; - } - } - // adds the instruction to the bytecode of the method - if ((var > 255) || (increment > 127) || (increment < -128)) { - code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var) - .putShort(increment); - } else { - code.putByte(Opcodes.IINC).put11(var, increment); - } - } - - @Override - public void visitTableSwitchInsn(final int min, final int max, - final Label dflt, final Label... labels) { - lastCodeOffset = code.length; - // adds the instruction to the bytecode of the method - int source = code.length; - code.putByte(Opcodes.TABLESWITCH); - code.putByteArray(null, 0, (4 - code.length % 4) % 4); - dflt.put(this, code, source, true); - code.putInt(min).putInt(max); - for (int i = 0; i < labels.length; ++i) { - labels[i].put(this, code, source, true); - } - // updates currentBlock - visitSwitchInsn(dflt, labels); - } - - @Override - public void visitLookupSwitchInsn(final Label dflt, final int[] keys, - final Label[] labels) { - lastCodeOffset = code.length; - // adds the instruction to the bytecode of the method - int source = code.length; - code.putByte(Opcodes.LOOKUPSWITCH); - code.putByteArray(null, 0, (4 - code.length % 4) % 4); - dflt.put(this, code, source, true); - code.putInt(labels.length); - for (int i = 0; i < labels.length; ++i) { - code.putInt(keys[i]); - labels[i].put(this, code, source, true); - } - // updates currentBlock - visitSwitchInsn(dflt, labels); - } - - private void visitSwitchInsn(final Label dflt, final Label[] labels) { - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); - // adds current block successors - addSuccessor(Edge.NORMAL, dflt); - dflt.getFirst().status |= Label.TARGET; - for (int i = 0; i < labels.length; ++i) { - addSuccessor(Edge.NORMAL, labels[i]); - labels[i].getFirst().status |= Label.TARGET; - } - } else { - // updates current stack size (max stack size unchanged) - --stackSize; - // adds current block successors - addSuccessor(stackSize, dflt); - for (int i = 0; i < labels.length; ++i) { - addSuccessor(stackSize, labels[i]); - } - } - // ends current block - noSuccessor(); - } - } - - @Override - public void visitMultiANewArrayInsn(final String desc, final int dims) { - lastCodeOffset = code.length; - Item i = cw.newClassItem(desc); - // Label currentBlock = this.currentBlock; - if (currentBlock != null) { - if (compute == FRAMES) { - currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); - } else { - // updates current stack size (max stack size unchanged because - // stack size variation always negative or null) - stackSize += 1 - dims; - } - } - // adds the instruction to the bytecode of the method - code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); - } - - @Override - public AnnotationVisitor visitInsnAnnotation(int typeRef, - TypePath typePath, String desc, boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write target_type and target_info - typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8); - AnnotationWriter.putTarget(typeRef, typePath, bv); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, - bv.length - 2); - if (visible) { - aw.next = ctanns; - ctanns = aw; - } else { - aw.next = ictanns; - ictanns = aw; - } - return aw; - } - - @Override - public void visitTryCatchBlock(final Label start, final Label end, - final Label handler, final String type) { - ++handlerCount; - Handler h = new Handler(); - h.start = start; - h.end = end; - h.handler = handler; - h.desc = type; - h.type = type != null ? cw.newClass(type) : 0; - if (lastHandler == null) { - firstHandler = h; - } else { - lastHandler.next = h; - } - lastHandler = h; - } - - @Override - public AnnotationVisitor visitTryCatchAnnotation(int typeRef, - TypePath typePath, String desc, boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write target_type and target_info - AnnotationWriter.putTarget(typeRef, typePath, bv); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, - bv.length - 2); - if (visible) { - aw.next = ctanns; - ctanns = aw; - } else { - aw.next = ictanns; - ictanns = aw; - } - return aw; - } - - @Override - public void visitLocalVariable(final String name, final String desc, - final String signature, final Label start, final Label end, - final int index) { - if (signature != null) { - if (localVarType == null) { - localVarType = new ByteVector(); - } - ++localVarTypeCount; - localVarType.putShort(start.position) - .putShort(end.position - start.position) - .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature)) - .putShort(index); - } - if (localVar == null) { - localVar = new ByteVector(); - } - ++localVarCount; - localVar.putShort(start.position) - .putShort(end.position - start.position) - .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc)) - .putShort(index); - if (compute != NOTHING) { - // updates max locals - char c = desc.charAt(0); - int n = index + (c == 'J' || c == 'D' ? 2 : 1); - if (n > maxLocals) { - maxLocals = n; - } - } - } - - @Override - public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, - TypePath typePath, Label[] start, Label[] end, int[] index, - String desc, boolean visible) { - if (!ClassReader.ANNOTATIONS) { - return null; - } - ByteVector bv = new ByteVector(); - // write target_type and target_info - bv.putByte(typeRef >>> 24).putShort(start.length); - for (int i = 0; i < start.length; ++i) { - bv.putShort(start[i].position) - .putShort(end[i].position - start[i].position) - .putShort(index[i]); - } - if (typePath == null) { - bv.putByte(0); - } else { - int length = typePath.b[typePath.offset] * 2 + 1; - bv.putByteArray(typePath.b, typePath.offset, length); - } - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, - bv.length - 2); - if (visible) { - aw.next = ctanns; - ctanns = aw; - } else { - aw.next = ictanns; - ictanns = aw; - } - return aw; - } - - @Override - public void visitLineNumber(final int line, final Label start) { - if (lineNumber == null) { - lineNumber = new ByteVector(); - } - ++lineNumberCount; - lineNumber.putShort(start.position); - lineNumber.putShort(line); - } - - @Override - public void visitMaxs(final int maxStack, final int maxLocals) { - if (resize) { - // replaces the temporary jump opcodes introduced by Label.resolve. - if (ClassReader.RESIZE) { - resizeInstructions(); - } else { - throw new RuntimeException("Method code too large!"); - } - } - if (ClassReader.FRAMES && compute == FRAMES) { - // completes the control flow graph with exception handler blocks - Handler handler = firstHandler; - while (handler != null) { - Label l = handler.start.getFirst(); - Label h = handler.handler.getFirst(); - Label e = handler.end.getFirst(); - // computes the kind of the edges to 'h' - String t = handler.desc == null ? "java/lang/Throwable" - : handler.desc; - int kind = Frame.OBJECT | cw.addType(t); - // h is an exception handler - h.status |= Label.TARGET; - // adds 'h' as a successor of labels between 'start' and 'end' - while (l != e) { - // creates an edge to 'h' - Edge b = new Edge(); - b.info = kind; - b.successor = h; - // adds it to the successors of 'l' - b.next = l.successors; - l.successors = b; - // goes to the next label - l = l.successor; - } - handler = handler.next; - } - - // creates and visits the first (implicit) frame - Frame f = labels.frame; - Type[] args = Type.getArgumentTypes(descriptor); - f.initInputFrame(cw, access, args, this.maxLocals); - visitFrame(f); - - /* - * fix point algorithm: mark the first basic block as 'changed' - * (i.e. put it in the 'changed' list) and, while there are changed - * basic blocks, choose one, mark it as unchanged, and update its - * successors (which can be changed in the process). - */ - int max = 0; - Label changed = labels; - while (changed != null) { - // removes a basic block from the list of changed basic blocks - Label l = changed; - changed = changed.next; - l.next = null; - f = l.frame; - // a reachable jump target must be stored in the stack map - if ((l.status & Label.TARGET) != 0) { - l.status |= Label.STORE; - } - // all visited labels are reachable, by definition - l.status |= Label.REACHABLE; - // updates the (absolute) maximum stack size - int blockMax = f.inputStack.length + l.outputStackMax; - if (blockMax > max) { - max = blockMax; - } - // updates the successors of the current basic block - Edge e = l.successors; - while (e != null) { - Label n = e.successor.getFirst(); - boolean change = f.merge(cw, n.frame, e.info); - if (change && n.next == null) { - // if n has changed and is not already in the 'changed' - // list, adds it to this list - n.next = changed; - changed = n; - } - e = e.next; - } - } - - // visits all the frames that must be stored in the stack map - Label l = labels; - while (l != null) { - f = l.frame; - if ((l.status & Label.STORE) != 0) { - visitFrame(f); - } - if ((l.status & Label.REACHABLE) == 0) { - // finds start and end of dead basic block - Label k = l.successor; - int start = l.position; - int end = (k == null ? code.length : k.position) - 1; - // if non empty basic block - if (end >= start) { - max = Math.max(max, 1); - // replaces instructions with NOP ... NOP ATHROW - for (int i = start; i < end; ++i) { - code.data[i] = Opcodes.NOP; - } - code.data[end] = (byte) Opcodes.ATHROW; - // emits a frame for this unreachable block - int frameIndex = startFrame(start, 0, 1); - frame[frameIndex] = Frame.OBJECT - | cw.addType("java/lang/Throwable"); - endFrame(); - // removes the start-end range from the exception - // handlers - firstHandler = Handler.remove(firstHandler, l, k); - } - } - l = l.successor; - } - - handler = firstHandler; - handlerCount = 0; - while (handler != null) { - handlerCount += 1; - handler = handler.next; - } - - this.maxStack = max; - } else if (compute == MAXS) { - // completes the control flow graph with exception handler blocks - Handler handler = firstHandler; - while (handler != null) { - Label l = handler.start; - Label h = handler.handler; - Label e = handler.end; - // adds 'h' as a successor of labels between 'start' and 'end' - while (l != e) { - // creates an edge to 'h' - Edge b = new Edge(); - b.info = Edge.EXCEPTION; - b.successor = h; - // adds it to the successors of 'l' - if ((l.status & Label.JSR) == 0) { - b.next = l.successors; - l.successors = b; - } else { - // if l is a JSR block, adds b after the first two edges - // to preserve the hypothesis about JSR block successors - // order (see {@link #visitJumpInsn}) - b.next = l.successors.next.next; - l.successors.next.next = b; - } - // goes to the next label - l = l.successor; - } - handler = handler.next; - } - - if (subroutines > 0) { - // completes the control flow graph with the RET successors - /* - * first step: finds the subroutines. This step determines, for - * each basic block, to which subroutine(s) it belongs. - */ - // finds the basic blocks that belong to the "main" subroutine - int id = 0; - labels.visitSubroutine(null, 1, subroutines); - // finds the basic blocks that belong to the real subroutines - Label l = labels; - while (l != null) { - if ((l.status & Label.JSR) != 0) { - // the subroutine is defined by l's TARGET, not by l - Label subroutine = l.successors.next.successor; - // if this subroutine has not been visited yet... - if ((subroutine.status & Label.VISITED) == 0) { - // ...assigns it a new id and finds its basic blocks - id += 1; - subroutine.visitSubroutine(null, (id / 32L) << 32 - | (1L << (id % 32)), subroutines); - } - } - l = l.successor; - } - // second step: finds the successors of RET blocks - l = labels; - while (l != null) { - if ((l.status & Label.JSR) != 0) { - Label L = labels; - while (L != null) { - L.status &= ~Label.VISITED2; - L = L.successor; - } - // the subroutine is defined by l's TARGET, not by l - Label subroutine = l.successors.next.successor; - subroutine.visitSubroutine(l, 0, subroutines); - } - l = l.successor; - } - } - - /* - * control flow analysis algorithm: while the block stack is not - * empty, pop a block from this stack, update the max stack size, - * compute the true (non relative) begin stack size of the - * successors of this block, and push these successors onto the - * stack (unless they have already been pushed onto the stack). - * Note: by hypothesis, the {@link Label#inputStackTop} of the - * blocks in the block stack are the true (non relative) beginning - * stack sizes of these blocks. - */ - int max = 0; - Label stack = labels; - while (stack != null) { - // pops a block from the stack - Label l = stack; - stack = stack.next; - // computes the true (non relative) max stack size of this block - int start = l.inputStackTop; - int blockMax = start + l.outputStackMax; - // updates the global max stack size - if (blockMax > max) { - max = blockMax; - } - // analyzes the successors of the block - Edge b = l.successors; - if ((l.status & Label.JSR) != 0) { - // ignores the first edge of JSR blocks (virtual successor) - b = b.next; - } - while (b != null) { - l = b.successor; - // if this successor has not already been pushed... - if ((l.status & Label.PUSHED) == 0) { - // computes its true beginning stack size... - l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start - + b.info; - // ...and pushes it onto the stack - l.status |= Label.PUSHED; - l.next = stack; - stack = l; - } - b = b.next; - } - } - this.maxStack = Math.max(maxStack, max); - } else { - this.maxStack = maxStack; - this.maxLocals = maxLocals; - } - } - - @Override - public void visitEnd() { - } - - // ------------------------------------------------------------------------ - // Utility methods: control flow analysis algorithm - // ------------------------------------------------------------------------ - - /** - * Adds a successor to the {@link #currentBlock currentBlock} block. - * - * @param info - * information about the control flow edge to be added. - * @param successor - * the successor block to be added to the current block. - */ - private void addSuccessor(final int info, final Label successor) { - // creates and initializes an Edge object... - Edge b = new Edge(); - b.info = info; - b.successor = successor; - // ...and adds it to the successor list of the currentBlock block - b.next = currentBlock.successors; - currentBlock.successors = b; - } - - /** - * Ends the current basic block. This method must be used in the case where - * the current basic block does not have any successor. - */ - private void noSuccessor() { - if (compute == FRAMES) { - Label l = new Label(); - l.frame = new Frame(); - l.frame.owner = l; - l.resolve(this, code.length, code.data); - previousBlock.successor = l; - previousBlock = l; - } else { - currentBlock.outputStackMax = maxStackSize; - } - currentBlock = null; - } - - // ------------------------------------------------------------------------ - // Utility methods: stack map frames - // ------------------------------------------------------------------------ - - /** - * Visits a frame that has been computed from scratch. - * - * @param f - * the frame that must be visited. - */ - private void visitFrame(final Frame f) { - int i, t; - int nTop = 0; - int nLocal = 0; - int nStack = 0; - int[] locals = f.inputLocals; - int[] stacks = f.inputStack; - // computes the number of locals (ignores TOP types that are just after - // a LONG or a DOUBLE, and all trailing TOP types) - for (i = 0; i < locals.length; ++i) { - t = locals[i]; - if (t == Frame.TOP) { - ++nTop; - } else { - nLocal += nTop + 1; - nTop = 0; - } - if (t == Frame.LONG || t == Frame.DOUBLE) { - ++i; - } - } - // computes the stack size (ignores TOP types that are just after - // a LONG or a DOUBLE) - for (i = 0; i < stacks.length; ++i) { - t = stacks[i]; - ++nStack; - if (t == Frame.LONG || t == Frame.DOUBLE) { - ++i; - } - } - // visits the frame and its content - int frameIndex = startFrame(f.owner.position, nLocal, nStack); - for (i = 0; nLocal > 0; ++i, --nLocal) { - t = locals[i]; - frame[frameIndex++] = t; - if (t == Frame.LONG || t == Frame.DOUBLE) { - ++i; - } - } - for (i = 0; i < stacks.length; ++i) { - t = stacks[i]; - frame[frameIndex++] = t; - if (t == Frame.LONG || t == Frame.DOUBLE) { - ++i; - } - } - endFrame(); - } - - /** - * Visit the implicit first frame of this method. - */ - private void visitImplicitFirstFrame() { - // There can be at most descriptor.length() + 1 locals - int frameIndex = startFrame(0, descriptor.length() + 1, 0); - if ((access & Opcodes.ACC_STATIC) == 0) { - if ((access & ACC_CONSTRUCTOR) == 0) { - frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName); - } else { - frame[frameIndex++] = 6; // Opcodes.UNINITIALIZED_THIS; - } - } - int i = 1; - loop: while (true) { - int j = i; - switch (descriptor.charAt(i++)) { - case 'Z': - case 'C': - case 'B': - case 'S': - case 'I': - frame[frameIndex++] = 1; // Opcodes.INTEGER; - break; - case 'F': - frame[frameIndex++] = 2; // Opcodes.FLOAT; - break; - case 'J': - frame[frameIndex++] = 4; // Opcodes.LONG; - break; - case 'D': - frame[frameIndex++] = 3; // Opcodes.DOUBLE; - break; - case '[': - while (descriptor.charAt(i) == '[') { - ++i; - } - if (descriptor.charAt(i) == 'L') { - ++i; - while (descriptor.charAt(i) != ';') { - ++i; - } - } - frame[frameIndex++] = Frame.OBJECT - | cw.addType(descriptor.substring(j, ++i)); - break; - case 'L': - while (descriptor.charAt(i) != ';') { - ++i; - } - frame[frameIndex++] = Frame.OBJECT - | cw.addType(descriptor.substring(j + 1, i++)); - break; - default: - break loop; - } - } - frame[1] = frameIndex - 3; - endFrame(); - } - - /** - * Starts the visit of a stack map frame. - * - * @param offset - * the offset of the instruction to which the frame corresponds. - * @param nLocal - * the number of local variables in the frame. - * @param nStack - * the number of stack elements in the frame. - * @return the index of the next element to be written in this frame. - */ - private int startFrame(final int offset, final int nLocal, final int nStack) { - int n = 3 + nLocal + nStack; - if (frame == null || frame.length < n) { - frame = new int[n]; - } - frame[0] = offset; - frame[1] = nLocal; - frame[2] = nStack; - return 3; - } - - /** - * Checks if the visit of the current frame {@link #frame} is finished, and - * if yes, write it in the StackMapTable attribute. - */ - private void endFrame() { - if (previousFrame != null) { // do not write the first frame - if (stackMap == null) { - stackMap = new ByteVector(); - } - writeFrame(); - ++frameCount; - } - previousFrame = frame; - frame = null; - } - - /** - * Compress and writes the current frame {@link #frame} in the StackMapTable - * attribute. - */ - private void writeFrame() { - int clocalsSize = frame[1]; - int cstackSize = frame[2]; - if ((cw.version & 0xFFFF) < Opcodes.V1_6) { - stackMap.putShort(frame[0]).putShort(clocalsSize); - writeFrameTypes(3, 3 + clocalsSize); - stackMap.putShort(cstackSize); - writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); - return; - } - int localsSize = previousFrame[1]; - int type = FULL_FRAME; - int k = 0; - int delta; - if (frameCount == 0) { - delta = frame[0]; - } else { - delta = frame[0] - previousFrame[0] - 1; - } - if (cstackSize == 0) { - k = clocalsSize - localsSize; - switch (k) { - case -3: - case -2: - case -1: - type = CHOP_FRAME; - localsSize = clocalsSize; - break; - case 0: - type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; - break; - case 1: - case 2: - case 3: - type = APPEND_FRAME; - break; - } - } else if (clocalsSize == localsSize && cstackSize == 1) { - type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME - : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; - } - if (type != FULL_FRAME) { - // verify if locals are the same - int l = 3; - for (int j = 0; j < localsSize; j++) { - if (frame[l] != previousFrame[l]) { - type = FULL_FRAME; - break; - } - l++; - } - } - switch (type) { - case SAME_FRAME: - stackMap.putByte(delta); - break; - case SAME_LOCALS_1_STACK_ITEM_FRAME: - stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); - writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); - break; - case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: - stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort( - delta); - writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); - break; - case SAME_FRAME_EXTENDED: - stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); - break; - case CHOP_FRAME: - stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); - break; - case APPEND_FRAME: - stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); - writeFrameTypes(3 + localsSize, 3 + clocalsSize); - break; - // case FULL_FRAME: - default: - stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize); - writeFrameTypes(3, 3 + clocalsSize); - stackMap.putShort(cstackSize); - writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); - } - } - - /** - * Writes some types of the current frame {@link #frame} into the - * StackMapTableAttribute. This method converts types from the format used - * in {@link Label} to the format used in StackMapTable attributes. In - * particular, it converts type table indexes to constant pool indexes. - * - * @param start - * index of the first type in {@link #frame} to write. - * @param end - * index of last type in {@link #frame} to write (exclusive). - */ - private void writeFrameTypes(final int start, final int end) { - for (int i = start; i < end; ++i) { - int t = frame[i]; - int d = t & Frame.DIM; - if (d == 0) { - int v = t & Frame.BASE_VALUE; - switch (t & Frame.BASE_KIND) { - case Frame.OBJECT: - stackMap.putByte(7).putShort( - cw.newClass(cw.typeTable[v].strVal1)); - break; - case Frame.UNINITIALIZED: - stackMap.putByte(8).putShort(cw.typeTable[v].intVal); - break; - default: - stackMap.putByte(v); - } - } else { - StringBuilder sb = new StringBuilder(); - d >>= 28; - while (d-- > 0) { - sb.append('['); - } - if ((t & Frame.BASE_KIND) == Frame.OBJECT) { - sb.append('L'); - sb.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); - sb.append(';'); - } else { - switch (t & 0xF) { - case 1: - sb.append('I'); - break; - case 2: - sb.append('F'); - break; - case 3: - sb.append('D'); - break; - case 9: - sb.append('Z'); - break; - case 10: - sb.append('B'); - break; - case 11: - sb.append('C'); - break; - case 12: - sb.append('S'); - break; - default: - sb.append('J'); - } - } - stackMap.putByte(7).putShort(cw.newClass(sb.toString())); - } - } - } - - private void writeFrameType(final Object type) { - if (type instanceof String) { - stackMap.putByte(7).putShort(cw.newClass((String) type)); - } else if (type instanceof Integer) { - stackMap.putByte(((Integer) type).intValue()); - } else { - stackMap.putByte(8).putShort(((Label) type).position); - } - } - - // ------------------------------------------------------------------------ - // Utility methods: dump bytecode array - // ------------------------------------------------------------------------ - - /** - * Returns the size of the bytecode of this method. - * - * @return the size of the bytecode of this method. - */ - final int getSize() { - if (classReaderOffset != 0) { - return 6 + classReaderLength; - } - int size = 8; - if (code.length > 0) { - if (code.length > 65536) { - throw new RuntimeException("Method code too large!"); - } - cw.newUTF8("Code"); - size += 18 + code.length + 8 * handlerCount; - if (localVar != null) { - cw.newUTF8("LocalVariableTable"); - size += 8 + localVar.length; - } - if (localVarType != null) { - cw.newUTF8("LocalVariableTypeTable"); - size += 8 + localVarType.length; - } - if (lineNumber != null) { - cw.newUTF8("LineNumberTable"); - size += 8 + lineNumber.length; - } - if (stackMap != null) { - boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; - cw.newUTF8(zip ? "StackMapTable" : "StackMap"); - size += 8 + stackMap.length; - } - if (ClassReader.ANNOTATIONS && ctanns != null) { - cw.newUTF8("RuntimeVisibleTypeAnnotations"); - size += 8 + ctanns.getSize(); - } - if (ClassReader.ANNOTATIONS && ictanns != null) { - cw.newUTF8("RuntimeInvisibleTypeAnnotations"); - size += 8 + ictanns.getSize(); - } - if (cattrs != null) { - size += cattrs.getSize(cw, code.data, code.length, maxStack, - maxLocals); - } - } - if (exceptionCount > 0) { - cw.newUTF8("Exceptions"); - size += 8 + 2 * exceptionCount; - } - if ((access & Opcodes.ACC_SYNTHETIC) != 0) { - if ((cw.version & 0xFFFF) < Opcodes.V1_5 - || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { - cw.newUTF8("Synthetic"); - size += 6; - } - } - if ((access & Opcodes.ACC_DEPRECATED) != 0) { - cw.newUTF8("Deprecated"); - size += 6; - } - if (ClassReader.SIGNATURES && signature != null) { - cw.newUTF8("Signature"); - cw.newUTF8(signature); - size += 8; - } - if (methodParameters != null) { - cw.newUTF8("MethodParameters"); - size += 7 + methodParameters.length; - } - if (ClassReader.ANNOTATIONS && annd != null) { - cw.newUTF8("AnnotationDefault"); - size += 6 + annd.length; - } - if (ClassReader.ANNOTATIONS && anns != null) { - cw.newUTF8("RuntimeVisibleAnnotations"); - size += 8 + anns.getSize(); - } - if (ClassReader.ANNOTATIONS && ianns != null) { - cw.newUTF8("RuntimeInvisibleAnnotations"); - size += 8 + ianns.getSize(); - } - if (ClassReader.ANNOTATIONS && tanns != null) { - cw.newUTF8("RuntimeVisibleTypeAnnotations"); - size += 8 + tanns.getSize(); - } - if (ClassReader.ANNOTATIONS && itanns != null) { - cw.newUTF8("RuntimeInvisibleTypeAnnotations"); - size += 8 + itanns.getSize(); - } - if (ClassReader.ANNOTATIONS && panns != null) { - cw.newUTF8("RuntimeVisibleParameterAnnotations"); - size += 7 + 2 * (panns.length - synthetics); - for (int i = panns.length - 1; i >= synthetics; --i) { - size += panns[i] == null ? 0 : panns[i].getSize(); - } - } - if (ClassReader.ANNOTATIONS && ipanns != null) { - cw.newUTF8("RuntimeInvisibleParameterAnnotations"); - size += 7 + 2 * (ipanns.length - synthetics); - for (int i = ipanns.length - 1; i >= synthetics; --i) { - size += ipanns[i] == null ? 0 : ipanns[i].getSize(); - } - } - if (attrs != null) { - size += attrs.getSize(cw, null, 0, -1, -1); - } - return size; - } - - /** - * Puts the bytecode of this method in the given byte vector. - * - * @param out - * the byte vector into which the bytecode of this method must be - * copied. - */ - final void put(final ByteVector out) { - final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; - int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED - | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE - | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); - out.putShort(access & ~mask).putShort(name).putShort(desc); - if (classReaderOffset != 0) { - out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); - return; - } - int attributeCount = 0; - if (code.length > 0) { - ++attributeCount; - } - if (exceptionCount > 0) { - ++attributeCount; - } - if ((access & Opcodes.ACC_SYNTHETIC) != 0) { - if ((cw.version & 0xFFFF) < Opcodes.V1_5 - || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { - ++attributeCount; - } - } - if ((access & Opcodes.ACC_DEPRECATED) != 0) { - ++attributeCount; - } - if (ClassReader.SIGNATURES && signature != null) { - ++attributeCount; - } - if (methodParameters != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && annd != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && anns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && ianns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && tanns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && itanns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && panns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && ipanns != null) { - ++attributeCount; - } - if (attrs != null) { - attributeCount += attrs.getCount(); - } - out.putShort(attributeCount); - if (code.length > 0) { - int size = 12 + code.length + 8 * handlerCount; - if (localVar != null) { - size += 8 + localVar.length; - } - if (localVarType != null) { - size += 8 + localVarType.length; - } - if (lineNumber != null) { - size += 8 + lineNumber.length; - } - if (stackMap != null) { - size += 8 + stackMap.length; - } - if (ClassReader.ANNOTATIONS && ctanns != null) { - size += 8 + ctanns.getSize(); - } - if (ClassReader.ANNOTATIONS && ictanns != null) { - size += 8 + ictanns.getSize(); - } - if (cattrs != null) { - size += cattrs.getSize(cw, code.data, code.length, maxStack, - maxLocals); - } - out.putShort(cw.newUTF8("Code")).putInt(size); - out.putShort(maxStack).putShort(maxLocals); - out.putInt(code.length).putByteArray(code.data, 0, code.length); - out.putShort(handlerCount); - if (handlerCount > 0) { - Handler h = firstHandler; - while (h != null) { - out.putShort(h.start.position).putShort(h.end.position) - .putShort(h.handler.position).putShort(h.type); - h = h.next; - } - } - attributeCount = 0; - if (localVar != null) { - ++attributeCount; - } - if (localVarType != null) { - ++attributeCount; - } - if (lineNumber != null) { - ++attributeCount; - } - if (stackMap != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && ctanns != null) { - ++attributeCount; - } - if (ClassReader.ANNOTATIONS && ictanns != null) { - ++attributeCount; - } - if (cattrs != null) { - attributeCount += cattrs.getCount(); - } - out.putShort(attributeCount); - if (localVar != null) { - out.putShort(cw.newUTF8("LocalVariableTable")); - out.putInt(localVar.length + 2).putShort(localVarCount); - out.putByteArray(localVar.data, 0, localVar.length); - } - if (localVarType != null) { - out.putShort(cw.newUTF8("LocalVariableTypeTable")); - out.putInt(localVarType.length + 2).putShort(localVarTypeCount); - out.putByteArray(localVarType.data, 0, localVarType.length); - } - if (lineNumber != null) { - out.putShort(cw.newUTF8("LineNumberTable")); - out.putInt(lineNumber.length + 2).putShort(lineNumberCount); - out.putByteArray(lineNumber.data, 0, lineNumber.length); - } - if (stackMap != null) { - boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; - out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); - out.putInt(stackMap.length + 2).putShort(frameCount); - out.putByteArray(stackMap.data, 0, stackMap.length); - } - if (ClassReader.ANNOTATIONS && ctanns != null) { - out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); - ctanns.put(out); - } - if (ClassReader.ANNOTATIONS && ictanns != null) { - out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); - ictanns.put(out); - } - if (cattrs != null) { - cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); - } - } - if (exceptionCount > 0) { - out.putShort(cw.newUTF8("Exceptions")).putInt( - 2 * exceptionCount + 2); - out.putShort(exceptionCount); - for (int i = 0; i < exceptionCount; ++i) { - out.putShort(exceptions[i]); - } - } - if ((access & Opcodes.ACC_SYNTHETIC) != 0) { - if ((cw.version & 0xFFFF) < Opcodes.V1_5 - || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { - out.putShort(cw.newUTF8("Synthetic")).putInt(0); - } - } - if ((access & Opcodes.ACC_DEPRECATED) != 0) { - out.putShort(cw.newUTF8("Deprecated")).putInt(0); - } - if (ClassReader.SIGNATURES && signature != null) { - out.putShort(cw.newUTF8("Signature")).putInt(2) - .putShort(cw.newUTF8(signature)); - } - if (methodParameters != null) { - out.putShort(cw.newUTF8("MethodParameters")); - out.putInt(methodParameters.length + 1).putByte( - methodParametersCount); - out.putByteArray(methodParameters.data, 0, methodParameters.length); - } - if (ClassReader.ANNOTATIONS && annd != null) { - out.putShort(cw.newUTF8("AnnotationDefault")); - out.putInt(annd.length); - out.putByteArray(annd.data, 0, annd.length); - } - if (ClassReader.ANNOTATIONS && anns != null) { - out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); - anns.put(out); - } - if (ClassReader.ANNOTATIONS && ianns != null) { - out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); - ianns.put(out); - } - if (ClassReader.ANNOTATIONS && tanns != null) { - out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); - tanns.put(out); - } - if (ClassReader.ANNOTATIONS && itanns != null) { - out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); - itanns.put(out); - } - if (ClassReader.ANNOTATIONS && panns != null) { - out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); - AnnotationWriter.put(panns, synthetics, out); - } - if (ClassReader.ANNOTATIONS && ipanns != null) { - out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); - AnnotationWriter.put(ipanns, synthetics, out); - } - if (attrs != null) { - attrs.put(cw, null, 0, -1, -1, out); - } - } - - // ------------------------------------------------------------------------ - // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) - // ------------------------------------------------------------------------ - - /** - * Resizes and replaces the temporary instructions inserted by - * {@link Label#resolve} for wide forward jumps, while keeping jump offsets - * and instruction addresses consistent. This may require to resize other - * existing instructions, or even to introduce new instructions: for - * example, increasing the size of an instruction by 2 at the middle of a - * method can increases the offset of an IFEQ instruction from 32766 to - * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W - * 32765. This, in turn, may require to increase the size of another jump - * instruction, and so on... All these operations are handled automatically - * by this method. - *

- * This method must be called after all the method that is being built - * has been visited. In particular, the {@link Label Label} objects used - * to construct the method are no longer valid after this method has been - * called. - */ - private void resizeInstructions() { - byte[] b = code.data; // bytecode of the method - int u, v, label; // indexes in b - int i, j; // loop indexes - /* - * 1st step: As explained above, resizing an instruction may require to - * resize another one, which may require to resize yet another one, and - * so on. The first step of the algorithm consists in finding all the - * instructions that need to be resized, without modifying the code. - * This is done by the following "fix point" algorithm: - * - * Parse the code to find the jump instructions whose offset will need - * more than 2 bytes to be stored (the future offset is computed from - * the current offset and from the number of bytes that will be inserted - * or removed between the source and target instructions). For each such - * instruction, adds an entry in (a copy of) the indexes and sizes - * arrays (if this has not already been done in a previous iteration!). - * - * If at least one entry has been added during the previous step, go - * back to the beginning, otherwise stop. - * - * In fact the real algorithm is complicated by the fact that the size - * of TABLESWITCH and LOOKUPSWITCH instructions depends on their - * position in the bytecode (because of padding). In order to ensure the - * convergence of the algorithm, the number of bytes to be added or - * removed from these instructions is over estimated during the previous - * loop, and computed exactly only after the loop is finished (this - * requires another pass to parse the bytecode of the method). - */ - int[] allIndexes = new int[0]; // copy of indexes - int[] allSizes = new int[0]; // copy of sizes - boolean[] resize; // instructions to be resized - int newOffset; // future offset of a jump instruction - - resize = new boolean[code.length]; - - // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done - int state = 3; - do { - if (state == 3) { - state = 2; - } - u = 0; - while (u < b.length) { - int opcode = b[u] & 0xFF; // opcode of current instruction - int insert = 0; // bytes to be added after this instruction - - switch (ClassWriter.TYPE[opcode]) { - case ClassWriter.NOARG_INSN: - case ClassWriter.IMPLVAR_INSN: - u += 1; - break; - case ClassWriter.LABEL_INSN: - if (opcode > 201) { - // converts temporary opcodes 202 to 217, 218 and - // 219 to IFEQ ... JSR (inclusive), IFNULL and - // IFNONNULL - opcode = opcode < 218 ? opcode - 49 : opcode - 20; - label = u + readUnsignedShort(b, u + 1); - } else { - label = u + readShort(b, u + 1); - } - newOffset = getNewOffset(allIndexes, allSizes, u, label); - if (newOffset < Short.MIN_VALUE - || newOffset > Short.MAX_VALUE) { - if (!resize[u]) { - if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) { - // two additional bytes will be required to - // replace this GOTO or JSR instruction with - // a GOTO_W or a JSR_W - insert = 2; - } else { - // five additional bytes will be required to - // replace this IFxxx instruction with - // IFNOTxxx GOTO_W , where IFNOTxxx - // is the "opposite" opcode of IFxxx (i.e., - // IFNE for IFEQ) and where designates - // the instruction just after the GOTO_W. - insert = 5; - } - resize[u] = true; - } - } - u += 3; - break; - case ClassWriter.LABELW_INSN: - u += 5; - break; - case ClassWriter.TABL_INSN: - if (state == 1) { - // true number of bytes to be added (or removed) - // from this instruction = (future number of padding - // bytes - current number of padding byte) - - // previously over estimated variation = - // = ((3 - newOffset%4) - (3 - u%4)) - u%4 - // = (-newOffset%4 + u%4) - u%4 - // = -(newOffset & 3) - newOffset = getNewOffset(allIndexes, allSizes, 0, u); - insert = -(newOffset & 3); - } else if (!resize[u]) { - // over estimation of the number of bytes to be - // added to this instruction = 3 - current number - // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 - insert = u & 3; - resize[u] = true; - } - // skips instruction - u = u + 4 - (u & 3); - u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; - break; - case ClassWriter.LOOK_INSN: - if (state == 1) { - // like TABL_INSN - newOffset = getNewOffset(allIndexes, allSizes, 0, u); - insert = -(newOffset & 3); - } else if (!resize[u]) { - // like TABL_INSN - insert = u & 3; - resize[u] = true; - } - // skips instruction - u = u + 4 - (u & 3); - u += 8 * readInt(b, u + 4) + 8; - break; - case ClassWriter.WIDE_INSN: - opcode = b[u + 1] & 0xFF; - if (opcode == Opcodes.IINC) { - u += 6; - } else { - u += 4; - } - break; - case ClassWriter.VAR_INSN: - case ClassWriter.SBYTE_INSN: - case ClassWriter.LDC_INSN: - u += 2; - break; - case ClassWriter.SHORT_INSN: - case ClassWriter.LDCW_INSN: - case ClassWriter.FIELDORMETH_INSN: - case ClassWriter.TYPE_INSN: - case ClassWriter.IINC_INSN: - u += 3; - break; - case ClassWriter.ITFMETH_INSN: - case ClassWriter.INDYMETH_INSN: - u += 5; - break; - // case ClassWriter.MANA_INSN: - default: - u += 4; - break; - } - if (insert != 0) { - // adds a new (u, insert) entry in the allIndexes and - // allSizes arrays - int[] newIndexes = new int[allIndexes.length + 1]; - int[] newSizes = new int[allSizes.length + 1]; - System.arraycopy(allIndexes, 0, newIndexes, 0, - allIndexes.length); - System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); - newIndexes[allIndexes.length] = u; - newSizes[allSizes.length] = insert; - allIndexes = newIndexes; - allSizes = newSizes; - if (insert > 0) { - state = 3; - } - } - } - if (state < 3) { - --state; - } - } while (state != 0); - - // 2nd step: - // copies the bytecode of the method into a new bytevector, updates the - // offsets, and inserts (or removes) bytes as requested. - - ByteVector newCode = new ByteVector(code.length); - - u = 0; - while (u < code.length) { - int opcode = b[u] & 0xFF; - switch (ClassWriter.TYPE[opcode]) { - case ClassWriter.NOARG_INSN: - case ClassWriter.IMPLVAR_INSN: - newCode.putByte(opcode); - u += 1; - break; - case ClassWriter.LABEL_INSN: - if (opcode > 201) { - // changes temporary opcodes 202 to 217 (inclusive), 218 - // and 219 to IFEQ ... JSR (inclusive), IFNULL and - // IFNONNULL - opcode = opcode < 218 ? opcode - 49 : opcode - 20; - label = u + readUnsignedShort(b, u + 1); - } else { - label = u + readShort(b, u + 1); - } - newOffset = getNewOffset(allIndexes, allSizes, u, label); - if (resize[u]) { - // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx - // with IFNOTxxx GOTO_W , where IFNOTxxx is - // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) - // and where designates the instruction just after - // the GOTO_W. - if (opcode == Opcodes.GOTO) { - newCode.putByte(200); // GOTO_W - } else if (opcode == Opcodes.JSR) { - newCode.putByte(201); // JSR_W - } else { - newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 - : opcode ^ 1); - newCode.putShort(8); // jump offset - newCode.putByte(200); // GOTO_W - // newOffset now computed from start of GOTO_W - newOffset -= 3; - } - newCode.putInt(newOffset); - } else { - newCode.putByte(opcode); - newCode.putShort(newOffset); - } - u += 3; - break; - case ClassWriter.LABELW_INSN: - label = u + readInt(b, u + 1); - newOffset = getNewOffset(allIndexes, allSizes, u, label); - newCode.putByte(opcode); - newCode.putInt(newOffset); - u += 5; - break; - case ClassWriter.TABL_INSN: - // skips 0 to 3 padding bytes - v = u; - u = u + 4 - (v & 3); - // reads and copies instruction - newCode.putByte(Opcodes.TABLESWITCH); - newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4); - label = v + readInt(b, u); - u += 4; - newOffset = getNewOffset(allIndexes, allSizes, v, label); - newCode.putInt(newOffset); - j = readInt(b, u); - u += 4; - newCode.putInt(j); - j = readInt(b, u) - j + 1; - u += 4; - newCode.putInt(readInt(b, u - 4)); - for (; j > 0; --j) { - label = v + readInt(b, u); - u += 4; - newOffset = getNewOffset(allIndexes, allSizes, v, label); - newCode.putInt(newOffset); - } - break; - case ClassWriter.LOOK_INSN: - // skips 0 to 3 padding bytes - v = u; - u = u + 4 - (v & 3); - // reads and copies instruction - newCode.putByte(Opcodes.LOOKUPSWITCH); - newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4); - label = v + readInt(b, u); - u += 4; - newOffset = getNewOffset(allIndexes, allSizes, v, label); - newCode.putInt(newOffset); - j = readInt(b, u); - u += 4; - newCode.putInt(j); - for (; j > 0; --j) { - newCode.putInt(readInt(b, u)); - u += 4; - label = v + readInt(b, u); - u += 4; - newOffset = getNewOffset(allIndexes, allSizes, v, label); - newCode.putInt(newOffset); - } - break; - case ClassWriter.WIDE_INSN: - opcode = b[u + 1] & 0xFF; - if (opcode == Opcodes.IINC) { - newCode.putByteArray(b, u, 6); - u += 6; - } else { - newCode.putByteArray(b, u, 4); - u += 4; - } - break; - case ClassWriter.VAR_INSN: - case ClassWriter.SBYTE_INSN: - case ClassWriter.LDC_INSN: - newCode.putByteArray(b, u, 2); - u += 2; - break; - case ClassWriter.SHORT_INSN: - case ClassWriter.LDCW_INSN: - case ClassWriter.FIELDORMETH_INSN: - case ClassWriter.TYPE_INSN: - case ClassWriter.IINC_INSN: - newCode.putByteArray(b, u, 3); - u += 3; - break; - case ClassWriter.ITFMETH_INSN: - case ClassWriter.INDYMETH_INSN: - newCode.putByteArray(b, u, 5); - u += 5; - break; - // case MANA_INSN: - default: - newCode.putByteArray(b, u, 4); - u += 4; - break; - } - } - - // updates the stack map frame labels - if (compute == FRAMES) { - Label l = labels; - while (l != null) { - /* - * Detects the labels that are just after an IF instruction that - * has been resized with the IFNOT GOTO_W pattern. These labels - * are now the target of a jump instruction (the IFNOT - * instruction). Note that we need the original label position - * here. getNewOffset must therefore never have been called for - * this label. - */ - u = l.position - 3; - if (u >= 0 && resize[u]) { - l.status |= Label.TARGET; - } - getNewOffset(allIndexes, allSizes, l); - l = l.successor; - } - // Update the offsets in the uninitialized types - if (cw.typeTable != null) { - for (i = 0; i < cw.typeTable.length; ++i) { - Item item = cw.typeTable[i]; - if (item != null && item.type == ClassWriter.TYPE_UNINIT) { - item.intVal = getNewOffset(allIndexes, allSizes, 0, - item.intVal); - } - } - } - // The stack map frames are not serialized yet, so we don't need - // to update them. They will be serialized in visitMaxs. - } else if (frameCount > 0) { - /* - * Resizing an existing stack map frame table is really hard. Not - * only the table must be parsed to update the offets, but new - * frames may be needed for jump instructions that were inserted by - * this method. And updating the offsets or inserting frames can - * change the format of the following frames, in case of packed - * frames. In practice the whole table must be recomputed. For this - * the frames are marked as potentially invalid. This will cause the - * whole class to be reread and rewritten with the COMPUTE_FRAMES - * option (see the ClassWriter.toByteArray method). This is not very - * efficient but is much easier and requires much less code than any - * other method I can think of. - */ - cw.invalidFrames = true; - } - // updates the exception handler block labels - Handler h = firstHandler; - while (h != null) { - getNewOffset(allIndexes, allSizes, h.start); - getNewOffset(allIndexes, allSizes, h.end); - getNewOffset(allIndexes, allSizes, h.handler); - h = h.next; - } - // updates the instructions addresses in the - // local var and line number tables - for (i = 0; i < 2; ++i) { - ByteVector bv = i == 0 ? localVar : localVarType; - if (bv != null) { - b = bv.data; - u = 0; - while (u < bv.length) { - label = readUnsignedShort(b, u); - newOffset = getNewOffset(allIndexes, allSizes, 0, label); - writeShort(b, u, newOffset); - label += readUnsignedShort(b, u + 2); - newOffset = getNewOffset(allIndexes, allSizes, 0, label) - - newOffset; - writeShort(b, u + 2, newOffset); - u += 10; - } - } - } - if (lineNumber != null) { - b = lineNumber.data; - u = 0; - while (u < lineNumber.length) { - writeShort( - b, - u, - getNewOffset(allIndexes, allSizes, 0, - readUnsignedShort(b, u))); - u += 4; - } - } - // updates the labels of the other attributes - Attribute attr = cattrs; - while (attr != null) { - Label[] labels = attr.getLabels(); - if (labels != null) { - for (i = labels.length - 1; i >= 0; --i) { - getNewOffset(allIndexes, allSizes, labels[i]); - } - } - attr = attr.next; - } - - // replaces old bytecodes with new ones - code = newCode; - } - - /** - * Reads an unsigned short value in the given byte array. - * - * @param b - * a byte array. - * @param index - * the start index of the value to be read. - * @return the read value. - */ - static int readUnsignedShort(final byte[] b, final int index) { - return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); - } - - /** - * Reads a signed short value in the given byte array. - * - * @param b - * a byte array. - * @param index - * the start index of the value to be read. - * @return the read value. - */ - static short readShort(final byte[] b, final int index) { - return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); - } - - /** - * Reads a signed int value in the given byte array. - * - * @param b - * a byte array. - * @param index - * the start index of the value to be read. - * @return the read value. - */ - static int readInt(final byte[] b, final int index) { - return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) - | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); - } - - /** - * Writes a short value in the given byte array. - * - * @param b - * a byte array. - * @param index - * where the first byte of the short value must be written. - * @param s - * the value to be written in the given byte array. - */ - static void writeShort(final byte[] b, final int index, final int s) { - b[index] = (byte) (s >>> 8); - b[index + 1] = (byte) s; - } - - /** - * Computes the future value of a bytecode offset. - *

- * Note: it is possible to have several entries for the same instruction in - * the indexes and sizes: two entries (index=a,size=b) and - * (index=a,size=b') are equivalent to a single entry (index=a,size=b+b'). - * - * @param indexes - * current positions of the instructions to be resized. Each - * instruction must be designated by the index of its last - * byte, plus one (or, in other words, by the index of the - * first byte of the next instruction). - * @param sizes - * the number of bytes to be added to the above - * instructions. More precisely, for each i < len, - * sizes[i] bytes will be added at the end of the - * instruction designated by indexes[i] or, if - * sizes[i] is negative, the last | - * sizes[i]| bytes of the instruction will be removed - * (the instruction size must not become negative or - * null). - * @param begin - * index of the first byte of the source instruction. - * @param end - * index of the first byte of the target instruction. - * @return the future value of the given bytecode offset. - */ - static int getNewOffset(final int[] indexes, final int[] sizes, - final int begin, final int end) { - int offset = end - begin; - for (int i = 0; i < indexes.length; ++i) { - if (begin < indexes[i] && indexes[i] <= end) { - // forward jump - offset += sizes[i]; - } else if (end < indexes[i] && indexes[i] <= begin) { - // backward jump - offset -= sizes[i]; - } - } - return offset; - } - - /** - * Updates the offset of the given label. - * - * @param indexes - * current positions of the instructions to be resized. Each - * instruction must be designated by the index of its last - * byte, plus one (or, in other words, by the index of the - * first byte of the next instruction). - * @param sizes - * the number of bytes to be added to the above - * instructions. More precisely, for each i < len, - * sizes[i] bytes will be added at the end of the - * instruction designated by indexes[i] or, if - * sizes[i] is negative, the last | - * sizes[i]| bytes of the instruction will be removed - * (the instruction size must not become negative or - * null). - * @param label - * the label whose offset must be updated. - */ - static void getNewOffset(final int[] indexes, final int[] sizes, - final Label label) { - if ((label.status & Label.RESIZED) == 0) { - label.position = getNewOffset(indexes, sizes, 0, label.position); - label.status |= Label.RESIZED; - } - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Opcodes.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Opcodes.java deleted file mode 100644 index 6f723ebe..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Opcodes.java +++ /dev/null @@ -1,361 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * Defines the JVM opcodes, access flags and array type codes. This interface - * does not define all the JVM opcodes because some opcodes are automatically - * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced - * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n - * opcodes are therefore not defined in this interface. Likewise for LDC, - * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and - * JSR_W. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public interface Opcodes { - - // ASM API versions - - int ASM4 = 4 << 16 | 0 << 8 | 0; - int ASM5 = 5 << 16 | 0 << 8 | 0; - - // versions - - int V1_1 = 3 << 16 | 45; - int V1_2 = 0 << 16 | 46; - int V1_3 = 0 << 16 | 47; - int V1_4 = 0 << 16 | 48; - int V1_5 = 0 << 16 | 49; - int V1_6 = 0 << 16 | 50; - int V1_7 = 0 << 16 | 51; - int V1_8 = 0 << 16 | 52; - - // access flags - - int ACC_PUBLIC = 0x0001; // class, field, method - int ACC_PRIVATE = 0x0002; // class, field, method - int ACC_PROTECTED = 0x0004; // class, field, method - int ACC_STATIC = 0x0008; // field, method - int ACC_FINAL = 0x0010; // class, field, method, parameter - int ACC_SUPER = 0x0020; // class - int ACC_SYNCHRONIZED = 0x0020; // method - int ACC_VOLATILE = 0x0040; // field - int ACC_BRIDGE = 0x0040; // method - int ACC_VARARGS = 0x0080; // method - int ACC_TRANSIENT = 0x0080; // field - int ACC_NATIVE = 0x0100; // method - int ACC_INTERFACE = 0x0200; // class - int ACC_ABSTRACT = 0x0400; // class, method - int ACC_STRICT = 0x0800; // method - int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter - int ACC_ANNOTATION = 0x2000; // class - int ACC_ENUM = 0x4000; // class(?) field inner - int ACC_MANDATED = 0x8000; // parameter - - // ASM specific pseudo access flags - - int ACC_DEPRECATED = 0x20000; // class, field, method - - // types for NEWARRAY - - int T_BOOLEAN = 4; - int T_CHAR = 5; - int T_FLOAT = 6; - int T_DOUBLE = 7; - int T_BYTE = 8; - int T_SHORT = 9; - int T_INT = 10; - int T_LONG = 11; - - // tags for Handle - - int H_GETFIELD = 1; - int H_GETSTATIC = 2; - int H_PUTFIELD = 3; - int H_PUTSTATIC = 4; - int H_INVOKEVIRTUAL = 5; - int H_INVOKESTATIC = 6; - int H_INVOKESPECIAL = 7; - int H_NEWINVOKESPECIAL = 8; - int H_INVOKEINTERFACE = 9; - - // stack map frame types - - /** - * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. - */ - int F_NEW = -1; - - /** - * Represents a compressed frame with complete frame data. - */ - int F_FULL = 0; - - /** - * Represents a compressed frame where locals are the same as the locals in - * the previous frame, except that additional 1-3 locals are defined, and - * with an empty stack. - */ - int F_APPEND = 1; - - /** - * Represents a compressed frame where locals are the same as the locals in - * the previous frame, except that the last 1-3 locals are absent and with - * an empty stack. - */ - int F_CHOP = 2; - - /** - * Represents a compressed frame with exactly the same locals as the - * previous frame and with an empty stack. - */ - int F_SAME = 3; - - /** - * Represents a compressed frame with exactly the same locals as the - * previous frame and with a single value on the stack. - */ - int F_SAME1 = 4; - - Integer TOP = new Integer(0); - Integer INTEGER = new Integer(1); - Integer FLOAT = new Integer(2); - Integer DOUBLE = new Integer(3); - Integer LONG = new Integer(4); - Integer NULL = new Integer(5); - Integer UNINITIALIZED_THIS = new Integer(6); - - // opcodes // visit method (- = idem) - - int NOP = 0; // visitInsn - int ACONST_NULL = 1; // - - int ICONST_M1 = 2; // - - int ICONST_0 = 3; // - - int ICONST_1 = 4; // - - int ICONST_2 = 5; // - - int ICONST_3 = 6; // - - int ICONST_4 = 7; // - - int ICONST_5 = 8; // - - int LCONST_0 = 9; // - - int LCONST_1 = 10; // - - int FCONST_0 = 11; // - - int FCONST_1 = 12; // - - int FCONST_2 = 13; // - - int DCONST_0 = 14; // - - int DCONST_1 = 15; // - - int BIPUSH = 16; // visitIntInsn - int SIPUSH = 17; // - - int LDC = 18; // visitLdcInsn - // int LDC_W = 19; // - - // int LDC2_W = 20; // - - int ILOAD = 21; // visitVarInsn - int LLOAD = 22; // - - int FLOAD = 23; // - - int DLOAD = 24; // - - int ALOAD = 25; // - - // int ILOAD_0 = 26; // - - // int ILOAD_1 = 27; // - - // int ILOAD_2 = 28; // - - // int ILOAD_3 = 29; // - - // int LLOAD_0 = 30; // - - // int LLOAD_1 = 31; // - - // int LLOAD_2 = 32; // - - // int LLOAD_3 = 33; // - - // int FLOAD_0 = 34; // - - // int FLOAD_1 = 35; // - - // int FLOAD_2 = 36; // - - // int FLOAD_3 = 37; // - - // int DLOAD_0 = 38; // - - // int DLOAD_1 = 39; // - - // int DLOAD_2 = 40; // - - // int DLOAD_3 = 41; // - - // int ALOAD_0 = 42; // - - // int ALOAD_1 = 43; // - - // int ALOAD_2 = 44; // - - // int ALOAD_3 = 45; // - - int IALOAD = 46; // visitInsn - int LALOAD = 47; // - - int FALOAD = 48; // - - int DALOAD = 49; // - - int AALOAD = 50; // - - int BALOAD = 51; // - - int CALOAD = 52; // - - int SALOAD = 53; // - - int ISTORE = 54; // visitVarInsn - int LSTORE = 55; // - - int FSTORE = 56; // - - int DSTORE = 57; // - - int ASTORE = 58; // - - // int ISTORE_0 = 59; // - - // int ISTORE_1 = 60; // - - // int ISTORE_2 = 61; // - - // int ISTORE_3 = 62; // - - // int LSTORE_0 = 63; // - - // int LSTORE_1 = 64; // - - // int LSTORE_2 = 65; // - - // int LSTORE_3 = 66; // - - // int FSTORE_0 = 67; // - - // int FSTORE_1 = 68; // - - // int FSTORE_2 = 69; // - - // int FSTORE_3 = 70; // - - // int DSTORE_0 = 71; // - - // int DSTORE_1 = 72; // - - // int DSTORE_2 = 73; // - - // int DSTORE_3 = 74; // - - // int ASTORE_0 = 75; // - - // int ASTORE_1 = 76; // - - // int ASTORE_2 = 77; // - - // int ASTORE_3 = 78; // - - int IASTORE = 79; // visitInsn - int LASTORE = 80; // - - int FASTORE = 81; // - - int DASTORE = 82; // - - int AASTORE = 83; // - - int BASTORE = 84; // - - int CASTORE = 85; // - - int SASTORE = 86; // - - int POP = 87; // - - int POP2 = 88; // - - int DUP = 89; // - - int DUP_X1 = 90; // - - int DUP_X2 = 91; // - - int DUP2 = 92; // - - int DUP2_X1 = 93; // - - int DUP2_X2 = 94; // - - int SWAP = 95; // - - int IADD = 96; // - - int LADD = 97; // - - int FADD = 98; // - - int DADD = 99; // - - int ISUB = 100; // - - int LSUB = 101; // - - int FSUB = 102; // - - int DSUB = 103; // - - int IMUL = 104; // - - int LMUL = 105; // - - int FMUL = 106; // - - int DMUL = 107; // - - int IDIV = 108; // - - int LDIV = 109; // - - int FDIV = 110; // - - int DDIV = 111; // - - int IREM = 112; // - - int LREM = 113; // - - int FREM = 114; // - - int DREM = 115; // - - int INEG = 116; // - - int LNEG = 117; // - - int FNEG = 118; // - - int DNEG = 119; // - - int ISHL = 120; // - - int LSHL = 121; // - - int ISHR = 122; // - - int LSHR = 123; // - - int IUSHR = 124; // - - int LUSHR = 125; // - - int IAND = 126; // - - int LAND = 127; // - - int IOR = 128; // - - int LOR = 129; // - - int IXOR = 130; // - - int LXOR = 131; // - - int IINC = 132; // visitIincInsn - int I2L = 133; // visitInsn - int I2F = 134; // - - int I2D = 135; // - - int L2I = 136; // - - int L2F = 137; // - - int L2D = 138; // - - int F2I = 139; // - - int F2L = 140; // - - int F2D = 141; // - - int D2I = 142; // - - int D2L = 143; // - - int D2F = 144; // - - int I2B = 145; // - - int I2C = 146; // - - int I2S = 147; // - - int LCMP = 148; // - - int FCMPL = 149; // - - int FCMPG = 150; // - - int DCMPL = 151; // - - int DCMPG = 152; // - - int IFEQ = 153; // visitJumpInsn - int IFNE = 154; // - - int IFLT = 155; // - - int IFGE = 156; // - - int IFGT = 157; // - - int IFLE = 158; // - - int IF_ICMPEQ = 159; // - - int IF_ICMPNE = 160; // - - int IF_ICMPLT = 161; // - - int IF_ICMPGE = 162; // - - int IF_ICMPGT = 163; // - - int IF_ICMPLE = 164; // - - int IF_ACMPEQ = 165; // - - int IF_ACMPNE = 166; // - - int GOTO = 167; // - - int JSR = 168; // - - int RET = 169; // visitVarInsn - int TABLESWITCH = 170; // visiTableSwitchInsn - int LOOKUPSWITCH = 171; // visitLookupSwitch - int IRETURN = 172; // visitInsn - int LRETURN = 173; // - - int FRETURN = 174; // - - int DRETURN = 175; // - - int ARETURN = 176; // - - int RETURN = 177; // - - int GETSTATIC = 178; // visitFieldInsn - int PUTSTATIC = 179; // - - int GETFIELD = 180; // - - int PUTFIELD = 181; // - - int INVOKEVIRTUAL = 182; // visitMethodInsn - int INVOKESPECIAL = 183; // - - int INVOKESTATIC = 184; // - - int INVOKEINTERFACE = 185; // - - int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn - int NEW = 187; // visitTypeInsn - int NEWARRAY = 188; // visitIntInsn - int ANEWARRAY = 189; // visitTypeInsn - int ARRAYLENGTH = 190; // visitInsn - int ATHROW = 191; // - - int CHECKCAST = 192; // visitTypeInsn - int INSTANCEOF = 193; // - - int MONITORENTER = 194; // visitInsn - int MONITOREXIT = 195; // - - // int WIDE = 196; // NOT VISITED - int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn - int IFNULL = 198; // visitJumpInsn - int IFNONNULL = 199; // - - // int GOTO_W = 200; // - - // int JSR_W = 201; // - -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Type.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Type.java deleted file mode 100644 index f454c623..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/Type.java +++ /dev/null @@ -1,896 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - -/** - * A Java field or method type. This class can be used to make it easier to - * manipulate type and method descriptors. - * - * @author Eric Bruneton - * @author Chris Nokleberg - */ -public class Type { - - /** - * The sort of the void type. See {@link #getSort getSort}. - */ - public static final int VOID = 0; - - /** - * The sort of the boolean type. See {@link #getSort getSort}. - */ - public static final int BOOLEAN = 1; - - /** - * The sort of the char type. See {@link #getSort getSort}. - */ - public static final int CHAR = 2; - - /** - * The sort of the byte type. See {@link #getSort getSort}. - */ - public static final int BYTE = 3; - - /** - * The sort of the short type. See {@link #getSort getSort}. - */ - public static final int SHORT = 4; - - /** - * The sort of the int type. See {@link #getSort getSort}. - */ - public static final int INT = 5; - - /** - * The sort of the float type. See {@link #getSort getSort}. - */ - public static final int FLOAT = 6; - - /** - * The sort of the long type. See {@link #getSort getSort}. - */ - public static final int LONG = 7; - - /** - * The sort of the double type. See {@link #getSort getSort}. - */ - public static final int DOUBLE = 8; - - /** - * The sort of array reference types. See {@link #getSort getSort}. - */ - public static final int ARRAY = 9; - - /** - * The sort of object reference types. See {@link #getSort getSort}. - */ - public static final int OBJECT = 10; - - /** - * The sort of method types. See {@link #getSort getSort}. - */ - public static final int METHOD = 11; - - /** - * The void type. - */ - public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24) - | (5 << 16) | (0 << 8) | 0, 1); - - /** - * The boolean type. - */ - public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24) - | (0 << 16) | (5 << 8) | 1, 1); - - /** - * The char type. - */ - public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24) - | (0 << 16) | (6 << 8) | 1, 1); - - /** - * The byte type. - */ - public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24) - | (0 << 16) | (5 << 8) | 1, 1); - - /** - * The short type. - */ - public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24) - | (0 << 16) | (7 << 8) | 1, 1); - - /** - * The int type. - */ - public static final Type INT_TYPE = new Type(INT, null, ('I' << 24) - | (0 << 16) | (0 << 8) | 1, 1); - - /** - * The float type. - */ - public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24) - | (2 << 16) | (2 << 8) | 1, 1); - - /** - * The long type. - */ - public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24) - | (1 << 16) | (1 << 8) | 2, 1); - - /** - * The double type. - */ - public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24) - | (3 << 16) | (3 << 8) | 2, 1); - - // ------------------------------------------------------------------------ - // Fields - // ------------------------------------------------------------------------ - - /** - * The sort of this Java type. - */ - private final int sort; - - /** - * A buffer containing the internal name of this Java type. This field is - * only used for reference types. - */ - private final char[] buf; - - /** - * The offset of the internal name of this Java type in {@link #buf buf} or, - * for primitive types, the size, descriptor and getOpcode offsets for this - * type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset - * for IALOAD or IASTORE, byte 3 the offset for all other instructions). - */ - private final int off; - - /** - * The length of the internal name of this Java type. - */ - private final int len; - - // ------------------------------------------------------------------------ - // Constructors - // ------------------------------------------------------------------------ - - /** - * Constructs a reference type. - * - * @param sort - * the sort of the reference type to be constructed. - * @param buf - * a buffer containing the descriptor of the previous type. - * @param off - * the offset of this descriptor in the previous buffer. - * @param len - * the length of this descriptor. - */ - private Type(final int sort, final char[] buf, final int off, final int len) { - this.sort = sort; - this.buf = buf; - this.off = off; - this.len = len; - } - - /** - * Returns the Java type corresponding to the given type descriptor. - * - * @param typeDescriptor - * a field or method type descriptor. - * @return the Java type corresponding to the given type descriptor. - */ - public static Type getType(final String typeDescriptor) { - return getType(typeDescriptor.toCharArray(), 0); - } - - /** - * Returns the Java type corresponding to the given internal name. - * - * @param internalName - * an internal name. - * @return the Java type corresponding to the given internal name. - */ - public static Type getObjectType(final String internalName) { - char[] buf = internalName.toCharArray(); - return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length); - } - - /** - * Returns the Java type corresponding to the given method descriptor. - * Equivalent to Type.getType(methodDescriptor). - * - * @param methodDescriptor - * a method descriptor. - * @return the Java type corresponding to the given method descriptor. - */ - public static Type getMethodType(final String methodDescriptor) { - return getType(methodDescriptor.toCharArray(), 0); - } - - /** - * Returns the Java method type corresponding to the given argument and - * return types. - * - * @param returnType - * the return type of the method. - * @param argumentTypes - * the argument types of the method. - * @return the Java type corresponding to the given argument and return - * types. - */ - public static Type getMethodType(final Type returnType, - final Type... argumentTypes) { - return getType(getMethodDescriptor(returnType, argumentTypes)); - } - - /** - * Returns the Java type corresponding to the given class. - * - * @param c - * a class. - * @return the Java type corresponding to the given class. - */ - public static Type getType(final Class c) { - if (c.isPrimitive()) { - if (c == Integer.TYPE) { - return INT_TYPE; - } else if (c == Void.TYPE) { - return VOID_TYPE; - } else if (c == Boolean.TYPE) { - return BOOLEAN_TYPE; - } else if (c == Byte.TYPE) { - return BYTE_TYPE; - } else if (c == Character.TYPE) { - return CHAR_TYPE; - } else if (c == Short.TYPE) { - return SHORT_TYPE; - } else if (c == Double.TYPE) { - return DOUBLE_TYPE; - } else if (c == Float.TYPE) { - return FLOAT_TYPE; - } else /* if (c == Long.TYPE) */{ - return LONG_TYPE; - } - } else { - return getType(getDescriptor(c)); - } - } - - /** - * Returns the Java method type corresponding to the given constructor. - * - * @param c - * a {@link Constructor Constructor} object. - * @return the Java method type corresponding to the given constructor. - */ - public static Type getType(final Constructor c) { - return getType(getConstructorDescriptor(c)); - } - - /** - * Returns the Java method type corresponding to the given method. - * - * @param m - * a {@link Method Method} object. - * @return the Java method type corresponding to the given method. - */ - public static Type getType(final Method m) { - return getType(getMethodDescriptor(m)); - } - - /** - * Returns the Java types corresponding to the argument types of the given - * method descriptor. - * - * @param methodDescriptor - * a method descriptor. - * @return the Java types corresponding to the argument types of the given - * method descriptor. - */ - public static Type[] getArgumentTypes(final String methodDescriptor) { - char[] buf = methodDescriptor.toCharArray(); - int off = 1; - int size = 0; - while (true) { - char car = buf[off++]; - if (car == ')') { - break; - } else if (car == 'L') { - while (buf[off++] != ';') { - } - ++size; - } else if (car != '[') { - ++size; - } - } - Type[] args = new Type[size]; - off = 1; - size = 0; - while (buf[off] != ')') { - args[size] = getType(buf, off); - off += args[size].len + (args[size].sort == OBJECT ? 2 : 0); - size += 1; - } - return args; - } - - /** - * Returns the Java types corresponding to the argument types of the given - * method. - * - * @param method - * a method. - * @return the Java types corresponding to the argument types of the given - * method. - */ - public static Type[] getArgumentTypes(final Method method) { - Class[] classes = method.getParameterTypes(); - Type[] types = new Type[classes.length]; - for (int i = classes.length - 1; i >= 0; --i) { - types[i] = getType(classes[i]); - } - return types; - } - - /** - * Returns the Java type corresponding to the return type of the given - * method descriptor. - * - * @param methodDescriptor - * a method descriptor. - * @return the Java type corresponding to the return type of the given - * method descriptor. - */ - public static Type getReturnType(final String methodDescriptor) { - char[] buf = methodDescriptor.toCharArray(); - return getType(buf, methodDescriptor.indexOf(')') + 1); - } - - /** - * Returns the Java type corresponding to the return type of the given - * method. - * - * @param method - * a method. - * @return the Java type corresponding to the return type of the given - * method. - */ - public static Type getReturnType(final Method method) { - return getType(method.getReturnType()); - } - - /** - * Computes the size of the arguments and of the return value of a method. - * - * @param desc - * the descriptor of a method. - * @return the size of the arguments of the method (plus one for the - * implicit this argument), argSize, and the size of its return - * value, retSize, packed into a single int i = - * (argSize << 2) | retSize (argSize is therefore equal to - * i >> 2, and retSize to i & 0x03). - */ - public static int getArgumentsAndReturnSizes(final String desc) { - int n = 1; - int c = 1; - while (true) { - char car = desc.charAt(c++); - if (car == ')') { - car = desc.charAt(c); - return n << 2 - | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); - } else if (car == 'L') { - while (desc.charAt(c++) != ';') { - } - n += 1; - } else if (car == '[') { - while ((car = desc.charAt(c)) == '[') { - ++c; - } - if (car == 'D' || car == 'J') { - n -= 1; - } - } else if (car == 'D' || car == 'J') { - n += 2; - } else { - n += 1; - } - } - } - - /** - * Returns the Java type corresponding to the given type descriptor. For - * method descriptors, buf is supposed to contain nothing more than the - * descriptor itself. - * - * @param buf - * a buffer containing a type descriptor. - * @param off - * the offset of this descriptor in the previous buffer. - * @return the Java type corresponding to the given type descriptor. - */ - private static Type getType(final char[] buf, final int off) { - int len; - switch (buf[off]) { - case 'V': - return VOID_TYPE; - case 'Z': - return BOOLEAN_TYPE; - case 'C': - return CHAR_TYPE; - case 'B': - return BYTE_TYPE; - case 'S': - return SHORT_TYPE; - case 'I': - return INT_TYPE; - case 'F': - return FLOAT_TYPE; - case 'J': - return LONG_TYPE; - case 'D': - return DOUBLE_TYPE; - case '[': - len = 1; - while (buf[off + len] == '[') { - ++len; - } - if (buf[off + len] == 'L') { - ++len; - while (buf[off + len] != ';') { - ++len; - } - } - return new Type(ARRAY, buf, off, len + 1); - case 'L': - len = 1; - while (buf[off + len] != ';') { - ++len; - } - return new Type(OBJECT, buf, off + 1, len - 1); - // case '(': - default: - return new Type(METHOD, buf, off, buf.length - off); - } - } - - // ------------------------------------------------------------------------ - // Accessors - // ------------------------------------------------------------------------ - - /** - * Returns the sort of this Java type. - * - * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR}, - * {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT}, - * {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE}, - * {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD - * METHOD}. - */ - public int getSort() { - return sort; - } - - /** - * Returns the number of dimensions of this array type. This method should - * only be used for an array type. - * - * @return the number of dimensions of this array type. - */ - public int getDimensions() { - int i = 1; - while (buf[off + i] == '[') { - ++i; - } - return i; - } - - /** - * Returns the type of the elements of this array type. This method should - * only be used for an array type. - * - * @return Returns the type of the elements of this array type. - */ - public Type getElementType() { - return getType(buf, off + getDimensions()); - } - - /** - * Returns the binary name of the class corresponding to this type. This - * method must not be used on method types. - * - * @return the binary name of the class corresponding to this type. - */ - public String getClassName() { - switch (sort) { - case VOID: - return "void"; - case BOOLEAN: - return "boolean"; - case CHAR: - return "char"; - case BYTE: - return "byte"; - case SHORT: - return "short"; - case INT: - return "int"; - case FLOAT: - return "float"; - case LONG: - return "long"; - case DOUBLE: - return "double"; - case ARRAY: - StringBuilder sb = new StringBuilder(getElementType().getClassName()); - for (int i = getDimensions(); i > 0; --i) { - sb.append("[]"); - } - return sb.toString(); - case OBJECT: - return new String(buf, off, len).replace('/', '.'); - default: - return null; - } - } - - /** - * Returns the internal name of the class corresponding to this object or - * array type. The internal name of a class is its fully qualified name (as - * returned by Class.getName(), where '.' are replaced by '/'. This method - * should only be used for an object or array type. - * - * @return the internal name of the class corresponding to this object type. - */ - public String getInternalName() { - return new String(buf, off, len); - } - - /** - * Returns the argument types of methods of this type. This method should - * only be used for method types. - * - * @return the argument types of methods of this type. - */ - public Type[] getArgumentTypes() { - return getArgumentTypes(getDescriptor()); - } - - /** - * Returns the return type of methods of this type. This method should only - * be used for method types. - * - * @return the return type of methods of this type. - */ - public Type getReturnType() { - return getReturnType(getDescriptor()); - } - - /** - * Returns the size of the arguments and of the return value of methods of - * this type. This method should only be used for method types. - * - * @return the size of the arguments (plus one for the implicit this - * argument), argSize, and the size of the return value, retSize, - * packed into a single - * int i = (argSize << 2) | retSize - * (argSize is therefore equal to i >> 2, - * and retSize to i & 0x03). - */ - public int getArgumentsAndReturnSizes() { - return getArgumentsAndReturnSizes(getDescriptor()); - } - - // ------------------------------------------------------------------------ - // Conversion to type descriptors - // ------------------------------------------------------------------------ - - /** - * Returns the descriptor corresponding to this Java type. - * - * @return the descriptor corresponding to this Java type. - */ - public String getDescriptor() { - StringBuffer buf = new StringBuffer(); - getDescriptor(buf); - return buf.toString(); - } - - /** - * Returns the descriptor corresponding to the given argument and return - * types. - * - * @param returnType - * the return type of the method. - * @param argumentTypes - * the argument types of the method. - * @return the descriptor corresponding to the given argument and return - * types. - */ - public static String getMethodDescriptor(final Type returnType, - final Type... argumentTypes) { - StringBuffer buf = new StringBuffer(); - buf.append('('); - for (int i = 0; i < argumentTypes.length; ++i) { - argumentTypes[i].getDescriptor(buf); - } - buf.append(')'); - returnType.getDescriptor(buf); - return buf.toString(); - } - - /** - * Appends the descriptor corresponding to this Java type to the given - * string buffer. - * - * @param buf - * the string buffer to which the descriptor must be appended. - */ - private void getDescriptor(final StringBuffer buf) { - if (this.buf == null) { - // descriptor is in byte 3 of 'off' for primitive types (buf == - // null) - buf.append((char) ((off & 0xFF000000) >>> 24)); - } else if (sort == OBJECT) { - buf.append('L'); - buf.append(this.buf, off, len); - buf.append(';'); - } else { // sort == ARRAY || sort == METHOD - buf.append(this.buf, off, len); - } - } - - // ------------------------------------------------------------------------ - // Direct conversion from classes to type descriptors, - // without intermediate Type objects - // ------------------------------------------------------------------------ - - /** - * Returns the internal name of the given class. The internal name of a - * class is its fully qualified name, as returned by Class.getName(), where - * '.' are replaced by '/'. - * - * @param c - * an object or array class. - * @return the internal name of the given class. - */ - public static String getInternalName(final Class c) { - return c.getName().replace('.', '/'); - } - - /** - * Returns the descriptor corresponding to the given Java type. - * - * @param c - * an object class, a primitive class or an array class. - * @return the descriptor corresponding to the given class. - */ - public static String getDescriptor(final Class c) { - StringBuffer buf = new StringBuffer(); - getDescriptor(buf, c); - return buf.toString(); - } - - /** - * Returns the descriptor corresponding to the given constructor. - * - * @param c - * a {@link Constructor Constructor} object. - * @return the descriptor of the given constructor. - */ - public static String getConstructorDescriptor(final Constructor c) { - Class[] parameters = c.getParameterTypes(); - StringBuffer buf = new StringBuffer(); - buf.append('('); - for (int i = 0; i < parameters.length; ++i) { - getDescriptor(buf, parameters[i]); - } - return buf.append(")V").toString(); - } - - /** - * Returns the descriptor corresponding to the given method. - * - * @param m - * a {@link Method Method} object. - * @return the descriptor of the given method. - */ - public static String getMethodDescriptor(final Method m) { - Class[] parameters = m.getParameterTypes(); - StringBuffer buf = new StringBuffer(); - buf.append('('); - for (int i = 0; i < parameters.length; ++i) { - getDescriptor(buf, parameters[i]); - } - buf.append(')'); - getDescriptor(buf, m.getReturnType()); - return buf.toString(); - } - - /** - * Appends the descriptor of the given class to the given string buffer. - * - * @param buf - * the string buffer to which the descriptor must be appended. - * @param c - * the class whose descriptor must be computed. - */ - private static void getDescriptor(final StringBuffer buf, final Class c) { - Class d = c; - while (true) { - if (d.isPrimitive()) { - char car; - if (d == Integer.TYPE) { - car = 'I'; - } else if (d == Void.TYPE) { - car = 'V'; - } else if (d == Boolean.TYPE) { - car = 'Z'; - } else if (d == Byte.TYPE) { - car = 'B'; - } else if (d == Character.TYPE) { - car = 'C'; - } else if (d == Short.TYPE) { - car = 'S'; - } else if (d == Double.TYPE) { - car = 'D'; - } else if (d == Float.TYPE) { - car = 'F'; - } else /* if (d == Long.TYPE) */{ - car = 'J'; - } - buf.append(car); - return; - } else if (d.isArray()) { - buf.append('['); - d = d.getComponentType(); - } else { - buf.append('L'); - String name = d.getName(); - int len = name.length(); - for (int i = 0; i < len; ++i) { - char car = name.charAt(i); - buf.append(car == '.' ? '/' : car); - } - buf.append(';'); - return; - } - } - } - - // ------------------------------------------------------------------------ - // Corresponding size and opcodes - // ------------------------------------------------------------------------ - - /** - * Returns the size of values of this type. This method must not be used for - * method types. - * - * @return the size of values of this type, i.e., 2 for long and - * double, 0 for void and 1 otherwise. - */ - public int getSize() { - // the size is in byte 0 of 'off' for primitive types (buf == null) - return buf == null ? (off & 0xFF) : 1; - } - - /** - * Returns a JVM instruction opcode adapted to this Java type. This method - * must not be used for method types. - * - * @param opcode - * a JVM instruction opcode. This opcode must be one of ILOAD, - * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, - * ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. - * @return an opcode that is similar to the given opcode, but adapted to - * this Java type. For example, if this type is float and - * opcode is IRETURN, this method returns FRETURN. - */ - public int getOpcode(final int opcode) { - if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { - // the offset for IALOAD or IASTORE is in byte 1 of 'off' for - // primitive types (buf == null) - return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4); - } else { - // the offset for other instructions is in byte 2 of 'off' for - // primitive types (buf == null) - return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4); - } - } - - // ------------------------------------------------------------------------ - // Equals, hashCode and toString - // ------------------------------------------------------------------------ - - /** - * Tests if the given object is equal to this type. - * - * @param o - * the object to be compared to this type. - * @return true if the given object is equal to this type. - */ - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (!(o instanceof Type)) { - return false; - } - Type t = (Type) o; - if (sort != t.sort) { - return false; - } - if (sort >= ARRAY) { - if (len != t.len) { - return false; - } - for (int i = off, j = t.off, end = i + len; i < end; i++, j++) { - if (buf[i] != t.buf[j]) { - return false; - } - } - } - return true; - } - - /** - * Returns a hash code value for this type. - * - * @return a hash code value for this type. - */ - @Override - public int hashCode() { - int hc = 13 * sort; - if (sort >= ARRAY) { - for (int i = off, end = i + len; i < end; i++) { - hc = 17 * (hc + buf[i]); - } - } - return hc; - } - - /** - * Returns a string representation of this type. - * - * @return the descriptor of this type. - */ - @Override - public String toString() { - return getDescriptor(); - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/TypePath.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/TypePath.java deleted file mode 100644 index 14ec85e7..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/TypePath.java +++ /dev/null @@ -1,196 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2013 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * The path to a type argument, wildcard bound, array element type, or static - * inner type within an enclosing type. - * - * @author Eric Bruneton - */ -public class TypePath { - - /** - * A type path step that steps into the element type of an array type. See - * {@link #getStep getStep}. - */ - public final static int ARRAY_ELEMENT = 0; - - /** - * A type path step that steps into the nested type of a class type. See - * {@link #getStep getStep}. - */ - public final static int INNER_TYPE = 1; - - /** - * A type path step that steps into the bound of a wildcard type. See - * {@link #getStep getStep}. - */ - public final static int WILDCARD_BOUND = 2; - - /** - * A type path step that steps into a type argument of a generic type. See - * {@link #getStep getStep}. - */ - public final static int TYPE_ARGUMENT = 3; - - /** - * The byte array where the path is stored, in Java class file format. - */ - byte[] b; - - /** - * The offset of the first byte of the type path in 'b'. - */ - int offset; - - /** - * Creates a new type path. - * - * @param b - * the byte array containing the type path in Java class file - * format. - * @param offset - * the offset of the first byte of the type path in 'b'. - */ - TypePath(byte[] b, int offset) { - this.b = b; - this.offset = offset; - } - - /** - * Returns the length of this path. - * - * @return the length of this path. - */ - public int getLength() { - return b[offset]; - } - - /** - * Returns the value of the given step of this path. - * - * @param index - * an index between 0 and {@link #getLength()}, exclusive. - * @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE - * INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or - * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. - */ - public int getStep(int index) { - return b[offset + 2 * index + 1]; - } - - /** - * Returns the index of the type argument that the given step is stepping - * into. This method should only be used for steps whose value is - * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. - * - * @param index - * an index between 0 and {@link #getLength()}, exclusive. - * @return the index of the type argument that the given step is stepping - * into. - */ - public int getStepArgument(int index) { - return b[offset + 2 * index + 2]; - } - - /** - * Converts a type path in string form, in the format used by - * {@link #toString()}, into a TypePath object. - * - * @param typePath - * a type path in string form, in the format used by - * {@link #toString()}. May be null or empty. - * @return the corresponding TypePath object, or null if the path is empty. - */ - public static TypePath fromString(final String typePath) { - if (typePath == null || typePath.length() == 0) { - return null; - } - int n = typePath.length(); - ByteVector out = new ByteVector(n); - out.putByte(0); - for (int i = 0; i < n;) { - char c = typePath.charAt(i++); - if (c == '[') { - out.put11(ARRAY_ELEMENT, 0); - } else if (c == '.') { - out.put11(INNER_TYPE, 0); - } else if (c == '*') { - out.put11(WILDCARD_BOUND, 0); - } else if (c >= '0' && c <= '9') { - int typeArg = c - '0'; - while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') { - typeArg = typeArg * 10 + c - '0'; - i += 1; - } - if (i < n && typePath.charAt(i) == ';') { - i += 1; - } - out.put11(TYPE_ARGUMENT, typeArg); - } - } - out.data[0] = (byte) (out.length / 2); - return new TypePath(out.data, 0); - } - - /** - * Returns a string representation of this type path. {@link #ARRAY_ELEMENT - * ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE - * INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps - * with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type - * argument index in decimal form followed by ';'. - */ - @Override - public String toString() { - int length = getLength(); - StringBuilder result = new StringBuilder(length * 2); - for (int i = 0; i < length; ++i) { - switch (getStep(i)) { - case ARRAY_ELEMENT: - result.append('['); - break; - case INNER_TYPE: - result.append('.'); - break; - case WILDCARD_BOUND: - result.append('*'); - break; - case TYPE_ARGUMENT: - result.append(getStepArgument(i)).append(';'); - break; - default: - result.append('_'); - } - } - return result.toString(); - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/TypeReference.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/TypeReference.java deleted file mode 100644 index 1e39031b..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/TypeReference.java +++ /dev/null @@ -1,452 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2013 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.javadeobfuscator.deobfuscator.org.objectweb.asm; - -/** - * A reference to a type appearing in a class, field or method declaration, or - * on an instruction. Such a reference designates the part of the class where - * the referenced type is appearing (e.g. an 'extends', 'implements' or 'throws' - * clause, a 'new' instruction, a 'catch' clause, a type cast, a local variable - * declaration, etc). - * - * @author Eric Bruneton - */ -public class TypeReference { - - /** - * The sort of type references that target a type parameter of a generic - * class. See {@link #getSort getSort}. - */ - public final static int CLASS_TYPE_PARAMETER = 0x00; - - /** - * The sort of type references that target a type parameter of a generic - * method. See {@link #getSort getSort}. - */ - public final static int METHOD_TYPE_PARAMETER = 0x01; - - /** - * The sort of type references that target the super class of a class or one - * of the interfaces it implements. See {@link #getSort getSort}. - */ - public final static int CLASS_EXTENDS = 0x10; - - /** - * The sort of type references that target a bound of a type parameter of a - * generic class. See {@link #getSort getSort}. - */ - public final static int CLASS_TYPE_PARAMETER_BOUND = 0x11; - - /** - * The sort of type references that target a bound of a type parameter of a - * generic method. See {@link #getSort getSort}. - */ - public final static int METHOD_TYPE_PARAMETER_BOUND = 0x12; - - /** - * The sort of type references that target the type of a field. See - * {@link #getSort getSort}. - */ - public final static int FIELD = 0x13; - - /** - * The sort of type references that target the return type of a method. See - * {@link #getSort getSort}. - */ - public final static int METHOD_RETURN = 0x14; - - /** - * The sort of type references that target the receiver type of a method. - * See {@link #getSort getSort}. - */ - public final static int METHOD_RECEIVER = 0x15; - - /** - * The sort of type references that target the type of a formal parameter of - * a method. See {@link #getSort getSort}. - */ - public final static int METHOD_FORMAL_PARAMETER = 0x16; - - /** - * The sort of type references that target the type of an exception declared - * in the throws clause of a method. See {@link #getSort getSort}. - */ - public final static int THROWS = 0x17; - - /** - * The sort of type references that target the type of a local variable in a - * method. See {@link #getSort getSort}. - */ - public final static int LOCAL_VARIABLE = 0x40; - - /** - * The sort of type references that target the type of a resource variable - * in a method. See {@link #getSort getSort}. - */ - public final static int RESOURCE_VARIABLE = 0x41; - - /** - * The sort of type references that target the type of the exception of a - * 'catch' clause in a method. See {@link #getSort getSort}. - */ - public final static int EXCEPTION_PARAMETER = 0x42; - - /** - * The sort of type references that target the type declared in an - * 'instanceof' instruction. See {@link #getSort getSort}. - */ - public final static int INSTANCEOF = 0x43; - - /** - * The sort of type references that target the type of the object created by - * a 'new' instruction. See {@link #getSort getSort}. - */ - public final static int NEW = 0x44; - - /** - * The sort of type references that target the receiver type of a - * constructor reference. See {@link #getSort getSort}. - */ - public final static int CONSTRUCTOR_REFERENCE = 0x45; - - /** - * The sort of type references that target the receiver type of a method - * reference. See {@link #getSort getSort}. - */ - public final static int METHOD_REFERENCE = 0x46; - - /** - * The sort of type references that target the type declared in an explicit - * or implicit cast instruction. See {@link #getSort getSort}. - */ - public final static int CAST = 0x47; - - /** - * The sort of type references that target a type parameter of a generic - * constructor in a constructor call. See {@link #getSort getSort}. - */ - public final static int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; - - /** - * The sort of type references that target a type parameter of a generic - * method in a method call. See {@link #getSort getSort}. - */ - public final static int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; - - /** - * The sort of type references that target a type parameter of a generic - * constructor in a constructor reference. See {@link #getSort getSort}. - */ - public final static int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; - - /** - * The sort of type references that target a type parameter of a generic - * method in a method reference. See {@link #getSort getSort}. - */ - public final static int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; - - /** - * The type reference value in Java class file format. - */ - private int value; - - /** - * Creates a new TypeReference. - * - * @param typeRef - * the int encoded value of the type reference, as received in a - * visit method related to type annotations, like - * visitTypeAnnotation. - */ - public TypeReference(int typeRef) { - this.value = typeRef; - } - - /** - * Returns a type reference of the given sort. - * - * @param sort - * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, - * {@link #METHOD_RECEIVER METHOD_RECEIVER}, - * {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, - * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, - * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, - * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, or - * {@link #METHOD_REFERENCE METHOD_REFERENCE}. - * @return a type reference of the given sort. - */ - public static TypeReference newTypeReference(int sort) { - return new TypeReference(sort << 24); - } - - /** - * Returns a reference to a type parameter of a generic class or method. - * - * @param sort - * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or - * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. - * @param paramIndex - * the type parameter index. - * @return a reference to the given generic class or method type parameter. - */ - public static TypeReference newTypeParameterReference(int sort, - int paramIndex) { - return new TypeReference((sort << 24) | (paramIndex << 16)); - } - - /** - * Returns a reference to a type parameter bound of a generic class or - * method. - * - * @param sort - * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or - * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. - * @param paramIndex - * the type parameter index. - * @param boundIndex - * the type bound index within the above type parameters. - * @return a reference to the given generic class or method type parameter - * bound. - */ - public static TypeReference newTypeParameterBoundReference(int sort, - int paramIndex, int boundIndex) { - return new TypeReference((sort << 24) | (paramIndex << 16) - | (boundIndex << 8)); - } - - /** - * Returns a reference to the super class or to an interface of the - * 'implements' clause of a class. - * - * @param itfIndex - * the index of an interface in the 'implements' clause of a - * class, or -1 to reference the super class of the class. - * @return a reference to the given super type of a class. - */ - public static TypeReference newSuperTypeReference(int itfIndex) { - itfIndex &= 0xFFFF; - return new TypeReference((CLASS_EXTENDS << 24) | (itfIndex << 8)); - } - - /** - * Returns a reference to the type of a formal parameter of a method. - * - * @param paramIndex - * the formal parameter index. - * - * @return a reference to the type of the given method formal parameter. - */ - public static TypeReference newFormalParameterReference(int paramIndex) { - return new TypeReference((METHOD_FORMAL_PARAMETER << 24) - | (paramIndex << 16)); - } - - /** - * Returns a reference to the type of an exception, in a 'throws' clause of - * a method. - * - * @param exceptionIndex - * the index of an exception in a 'throws' clause of a method. - * - * @return a reference to the type of the given exception. - */ - public static TypeReference newExceptionReference(int exceptionIndex) { - return new TypeReference((THROWS << 24) | (exceptionIndex << 8)); - } - - /** - * Returns a reference to the type of the exception declared in a 'catch' - * clause of a method. - * - * @param tryCatchBlockIndex - * the index of a try catch block (using the order in which they - * are visited with visitTryCatchBlock). - * - * @return a reference to the type of the given exception. - */ - public static TypeReference newTryCatchReference(int tryCatchBlockIndex) { - return new TypeReference((EXCEPTION_PARAMETER << 24) - | (tryCatchBlockIndex << 8)); - } - - /** - * Returns a reference to the type of a type argument in a constructor or - * method call or reference. - * - * @param sort - * {@link #CAST CAST}, - * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT - * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, - * {@link #METHOD_INVOCATION_TYPE_ARGUMENT - * METHOD_INVOCATION_TYPE_ARGUMENT}, - * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT - * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or - * {@link #METHOD_REFERENCE_TYPE_ARGUMENT - * METHOD_REFERENCE_TYPE_ARGUMENT}. - * @param argIndex - * the type argument index. - * - * @return a reference to the type of the given type argument. - */ - public static TypeReference newTypeArgumentReference(int sort, int argIndex) { - return new TypeReference((sort << 24) | argIndex); - } - - /** - * Returns the sort of this type reference. - * - * @return {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, - * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, - * {@link #CLASS_EXTENDS CLASS_EXTENDS}, - * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND}, - * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}, - * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, - * {@link #METHOD_RECEIVER METHOD_RECEIVER}, - * {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}, - * {@link #THROWS THROWS}, {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, - * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, - * {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER}, - * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, - * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, - * {@link #METHOD_REFERENCE METHOD_REFERENCE}, {@link #CAST CAST}, - * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT - * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, - * {@link #METHOD_INVOCATION_TYPE_ARGUMENT - * METHOD_INVOCATION_TYPE_ARGUMENT}, - * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT - * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or - * {@link #METHOD_REFERENCE_TYPE_ARGUMENT - * METHOD_REFERENCE_TYPE_ARGUMENT}. - */ - public int getSort() { - return value >>> 24; - } - - /** - * Returns the index of the type parameter referenced by this type - * reference. This method must only be used for type references whose sort - * is {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, - * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, - * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or - * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. - * - * @return a type parameter index. - */ - public int getTypeParameterIndex() { - return (value & 0x00FF0000) >> 16; - } - - /** - * Returns the index of the type parameter bound, within the type parameter - * {@link #getTypeParameterIndex}, referenced by this type reference. This - * method must only be used for type references whose sort is - * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or - * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. - * - * @return a type parameter bound index. - */ - public int getTypeParameterBoundIndex() { - return (value & 0x0000FF00) >> 8; - } - - /** - * Returns the index of the "super type" of a class that is referenced by - * this type reference. This method must only be used for type references - * whose sort is {@link #CLASS_EXTENDS CLASS_EXTENDS}. - * - * @return the index of an interface in the 'implements' clause of a class, - * or -1 if this type reference references the type of the super - * class. - */ - public int getSuperTypeIndex() { - return (short) ((value & 0x00FFFF00) >> 8); - } - - /** - * Returns the index of the formal parameter whose type is referenced by - * this type reference. This method must only be used for type references - * whose sort is {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}. - * - * @return a formal parameter index. - */ - public int getFormalParameterIndex() { - return (value & 0x00FF0000) >> 16; - } - - /** - * Returns the index of the exception, in a 'throws' clause of a method, - * whose type is referenced by this type reference. This method must only be - * used for type references whose sort is {@link #THROWS THROWS}. - * - * @return the index of an exception in the 'throws' clause of a method. - */ - public int getExceptionIndex() { - return (value & 0x00FFFF00) >> 8; - } - - /** - * Returns the index of the try catch block (using the order in which they - * are visited with visitTryCatchBlock), whose 'catch' type is referenced by - * this type reference. This method must only be used for type references - * whose sort is {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER} . - * - * @return the index of an exception in the 'throws' clause of a method. - */ - public int getTryCatchBlockIndex() { - return (value & 0x00FFFF00) >> 8; - } - - /** - * Returns the index of the type argument referenced by this type reference. - * This method must only be used for type references whose sort is - * {@link #CAST CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT - * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, - * {@link #METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT}, - * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT - * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or - * {@link #METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}. - * - * @return a type parameter index. - */ - public int getTypeArgumentIndex() { - return value & 0xFF; - } - - /** - * Returns the int encoded value of this type reference, suitable for use in - * visit methods related to type annotations, like visitTypeAnnotation. - * - * @return the int encoded value of this type reference. - */ - public int getValue() { - return value; - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/commons/AdviceAdapter.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/commons/AdviceAdapter.java deleted file mode 100644 index ff9d2c8e..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/commons/AdviceAdapter.java +++ /dev/null @@ -1,646 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm.commons; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Handle; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Label; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.MethodVisitor; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; - -/** - * A {@link com.javadeobfuscator.deobfuscator.org.objectweb.asm.MethodVisitor} to insert before, after and around - * advices in methods and constructors. - *

- * The behavior for constructors is like this: - *

    - * - *
  1. as long as the INVOKESPECIAL for the object initialization has not been - * reached, every bytecode instruction is dispatched in the ctor code visitor
  2. - * - *
  3. when this one is reached, it is only added in the ctor code visitor and a - * JP invoke is added
  4. - * - *
  5. after that, only the other code visitor receives the instructions
  6. - * - *
- * - * @author Eugene Kuleshov - * @author Eric Bruneton - */ -public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes { - - private static final Object THIS = new Object(); - - private static final Object OTHER = new Object(); - - protected int methodAccess; - - protected String methodDesc; - - private boolean constructor; - - private boolean superInitialized; - - private List stackFrame; - - private Map> branches; - - /** - * Creates a new {@link AdviceAdapter}. - * - * @param api - * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. - * @param mv - * the method visitor to which this adapter delegates calls. - * @param access - * the method's access flags (see {@link Opcodes}). - * @param name - * the method's name. - * @param desc - * the method's descriptor (see {@link Type Type}). - */ - protected AdviceAdapter(final int api, final MethodVisitor mv, - final int access, final String name, final String desc) { - super(api, mv, access, name, desc); - methodAccess = access; - methodDesc = desc; - constructor = "".equals(name); - } - - @Override - public void visitCode() { - mv.visitCode(); - if (constructor) { - stackFrame = new ArrayList(); - branches = new HashMap>(); - } else { - superInitialized = true; - onMethodEnter(); - } - } - - @Override - public void visitLabel(final Label label) { - mv.visitLabel(label); - if (constructor && branches != null) { - List frame = branches.get(label); - if (frame != null) { - stackFrame = frame; - branches.remove(label); - } - } - } - - @Override - public void visitInsn(final int opcode) { - if (constructor) { - int s; - switch (opcode) { - case RETURN: // empty stack - onMethodExit(opcode); - break; - case IRETURN: // 1 before n/a after - case FRETURN: // 1 before n/a after - case ARETURN: // 1 before n/a after - case ATHROW: // 1 before n/a after - popValue(); - onMethodExit(opcode); - break; - case LRETURN: // 2 before n/a after - case DRETURN: // 2 before n/a after - popValue(); - popValue(); - onMethodExit(opcode); - break; - case NOP: - case LALOAD: // remove 2 add 2 - case DALOAD: // remove 2 add 2 - case LNEG: - case DNEG: - case FNEG: - case INEG: - case L2D: - case D2L: - case F2I: - case I2B: - case I2C: - case I2S: - case I2F: - case ARRAYLENGTH: - break; - case ACONST_NULL: - case ICONST_M1: - case ICONST_0: - case ICONST_1: - case ICONST_2: - case ICONST_3: - case ICONST_4: - case ICONST_5: - case FCONST_0: - case FCONST_1: - case FCONST_2: - case F2L: // 1 before 2 after - case F2D: - case I2L: - case I2D: - pushValue(OTHER); - break; - case LCONST_0: - case LCONST_1: - case DCONST_0: - case DCONST_1: - pushValue(OTHER); - pushValue(OTHER); - break; - case IALOAD: // remove 2 add 1 - case FALOAD: // remove 2 add 1 - case AALOAD: // remove 2 add 1 - case BALOAD: // remove 2 add 1 - case CALOAD: // remove 2 add 1 - case SALOAD: // remove 2 add 1 - case POP: - case IADD: - case FADD: - case ISUB: - case LSHL: // 3 before 2 after - case LSHR: // 3 before 2 after - case LUSHR: // 3 before 2 after - case L2I: // 2 before 1 after - case L2F: // 2 before 1 after - case D2I: // 2 before 1 after - case D2F: // 2 before 1 after - case FSUB: - case FMUL: - case FDIV: - case FREM: - case FCMPL: // 2 before 1 after - case FCMPG: // 2 before 1 after - case IMUL: - case IDIV: - case IREM: - case ISHL: - case ISHR: - case IUSHR: - case IAND: - case IOR: - case IXOR: - case MONITORENTER: - case MONITOREXIT: - popValue(); - break; - case POP2: - case LSUB: - case LMUL: - case LDIV: - case LREM: - case LADD: - case LAND: - case LOR: - case LXOR: - case DADD: - case DMUL: - case DSUB: - case DDIV: - case DREM: - popValue(); - popValue(); - break; - case IASTORE: - case FASTORE: - case AASTORE: - case BASTORE: - case CASTORE: - case SASTORE: - case LCMP: // 4 before 1 after - case DCMPL: - case DCMPG: - popValue(); - popValue(); - popValue(); - break; - case LASTORE: - case DASTORE: - popValue(); - popValue(); - popValue(); - popValue(); - break; - case DUP: - pushValue(peekValue()); - break; - case DUP_X1: - s = stackFrame.size(); - stackFrame.add(s - 2, stackFrame.get(s - 1)); - break; - case DUP_X2: - s = stackFrame.size(); - stackFrame.add(s - 3, stackFrame.get(s - 1)); - break; - case DUP2: - s = stackFrame.size(); - stackFrame.add(s - 2, stackFrame.get(s - 1)); - stackFrame.add(s - 2, stackFrame.get(s - 1)); - break; - case DUP2_X1: - s = stackFrame.size(); - stackFrame.add(s - 3, stackFrame.get(s - 1)); - stackFrame.add(s - 3, stackFrame.get(s - 1)); - break; - case DUP2_X2: - s = stackFrame.size(); - stackFrame.add(s - 4, stackFrame.get(s - 1)); - stackFrame.add(s - 4, stackFrame.get(s - 1)); - break; - case SWAP: - s = stackFrame.size(); - stackFrame.add(s - 2, stackFrame.get(s - 1)); - stackFrame.remove(s); - break; - } - } else { - switch (opcode) { - case RETURN: - case IRETURN: - case FRETURN: - case ARETURN: - case LRETURN: - case DRETURN: - case ATHROW: - onMethodExit(opcode); - break; - } - } - mv.visitInsn(opcode); - } - - @Override - public void visitVarInsn(final int opcode, final int var) { - super.visitVarInsn(opcode, var); - if (constructor) { - switch (opcode) { - case ILOAD: - case FLOAD: - pushValue(OTHER); - break; - case LLOAD: - case DLOAD: - pushValue(OTHER); - pushValue(OTHER); - break; - case ALOAD: - pushValue(var == 0 ? THIS : OTHER); - break; - case ASTORE: - case ISTORE: - case FSTORE: - popValue(); - break; - case LSTORE: - case DSTORE: - popValue(); - popValue(); - break; - } - } - } - - @Override - public void visitFieldInsn(final int opcode, final String owner, - final String name, final String desc) { - mv.visitFieldInsn(opcode, owner, name, desc); - if (constructor) { - char c = desc.charAt(0); - boolean longOrDouble = c == 'J' || c == 'D'; - switch (opcode) { - case GETSTATIC: - pushValue(OTHER); - if (longOrDouble) { - pushValue(OTHER); - } - break; - case PUTSTATIC: - popValue(); - if (longOrDouble) { - popValue(); - } - break; - case PUTFIELD: - popValue(); - if (longOrDouble) { - popValue(); - popValue(); - } - break; - // case GETFIELD: - default: - if (longOrDouble) { - pushValue(OTHER); - } - } - } - } - - @Override - public void visitIntInsn(final int opcode, final int operand) { - mv.visitIntInsn(opcode, operand); - if (constructor && opcode != NEWARRAY) { - pushValue(OTHER); - } - } - - @Override - public void visitLdcInsn(final Object cst) { - mv.visitLdcInsn(cst); - if (constructor) { - pushValue(OTHER); - if (cst instanceof Double || cst instanceof Long) { - pushValue(OTHER); - } - } - } - - @Override - public void visitMultiANewArrayInsn(final String desc, final int dims) { - mv.visitMultiANewArrayInsn(desc, dims); - if (constructor) { - for (int i = 0; i < dims; i++) { - popValue(); - } - pushValue(OTHER); - } - } - - @Override - public void visitTypeInsn(final int opcode, final String type) { - mv.visitTypeInsn(opcode, type); - // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack - if (constructor && opcode == NEW) { - pushValue(OTHER); - } - } - - @Deprecated - @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { - if (api >= Opcodes.ASM5) { - super.visitMethodInsn(opcode, owner, name, desc); - return; - } - doVisitMethodInsn(opcode, owner, name, desc, - opcode == Opcodes.INVOKEINTERFACE); - } - - @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc, final boolean itf) { - if (api < Opcodes.ASM5) { - super.visitMethodInsn(opcode, owner, name, desc, itf); - return; - } - doVisitMethodInsn(opcode, owner, name, desc, itf); - } - - private void doVisitMethodInsn(int opcode, final String owner, - final String name, final String desc, final boolean itf) { - mv.visitMethodInsn(opcode, owner, name, desc, itf); - if (constructor) { - Type[] types = Type.getArgumentTypes(desc); - for (int i = 0; i < types.length; i++) { - popValue(); - if (types[i].getSize() == 2) { - popValue(); - } - } - switch (opcode) { - // case INVOKESTATIC: - // break; - case INVOKEINTERFACE: - case INVOKEVIRTUAL: - popValue(); // objectref - break; - case INVOKESPECIAL: - Object type = popValue(); // objectref - if (type == THIS && !superInitialized) { - onMethodEnter(); - superInitialized = true; - // once super has been initialized it is no longer - // necessary to keep track of stack state - constructor = false; - } - break; - } - - Type returnType = Type.getReturnType(desc); - if (returnType != Type.VOID_TYPE) { - pushValue(OTHER); - if (returnType.getSize() == 2) { - pushValue(OTHER); - } - } - } - } - - @Override - public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, - Object... bsmArgs) { - mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); - if (constructor) { - Type[] types = Type.getArgumentTypes(desc); - for (int i = 0; i < types.length; i++) { - popValue(); - if (types[i].getSize() == 2) { - popValue(); - } - } - - Type returnType = Type.getReturnType(desc); - if (returnType != Type.VOID_TYPE) { - pushValue(OTHER); - if (returnType.getSize() == 2) { - pushValue(OTHER); - } - } - } - } - - @Override - public void visitJumpInsn(final int opcode, final Label label) { - mv.visitJumpInsn(opcode, label); - if (constructor) { - switch (opcode) { - case IFEQ: - case IFNE: - case IFLT: - case IFGE: - case IFGT: - case IFLE: - case IFNULL: - case IFNONNULL: - popValue(); - break; - case IF_ICMPEQ: - case IF_ICMPNE: - case IF_ICMPLT: - case IF_ICMPGE: - case IF_ICMPGT: - case IF_ICMPLE: - case IF_ACMPEQ: - case IF_ACMPNE: - popValue(); - popValue(); - break; - case JSR: - pushValue(OTHER); - break; - } - addBranch(label); - } - } - - @Override - public void visitLookupSwitchInsn(final Label dflt, final int[] keys, - final Label[] labels) { - mv.visitLookupSwitchInsn(dflt, keys, labels); - if (constructor) { - popValue(); - addBranches(dflt, labels); - } - } - - @Override - public void visitTableSwitchInsn(final int min, final int max, - final Label dflt, final Label... labels) { - mv.visitTableSwitchInsn(min, max, dflt, labels); - if (constructor) { - popValue(); - addBranches(dflt, labels); - } - } - - @Override - public void visitTryCatchBlock(Label start, Label end, Label handler, - String type) { - super.visitTryCatchBlock(start, end, handler, type); - if (constructor && !branches.containsKey(handler)) { - List stackFrame = new ArrayList(); - stackFrame.add(OTHER); - branches.put(handler, stackFrame); - } - } - - private void addBranches(final Label dflt, final Label[] labels) { - addBranch(dflt); - for (int i = 0; i < labels.length; i++) { - addBranch(labels[i]); - } - } - - private void addBranch(final Label label) { - if (branches.containsKey(label)) { - return; - } - branches.put(label, new ArrayList(stackFrame)); - } - - private Object popValue() { - return stackFrame.remove(stackFrame.size() - 1); - } - - private Object peekValue() { - return stackFrame.get(stackFrame.size() - 1); - } - - private void pushValue(final Object o) { - stackFrame.add(o); - } - - /** - * Called at the beginning of the method or after super class call in - * the constructor.
- *
- * - * Custom code can use or change all the local variables, but should not - * change state of the stack. - */ - protected void onMethodEnter() { - } - - /** - * Called before explicit exit from the method using either return or throw. - * Top element on the stack contains the return value or exception instance. - * For example: - * - *
-     *   public void onMethodExit(int opcode) {
-     *     if(opcode==RETURN) {
-     *         visitInsn(ACONST_NULL);
-     *     } else if(opcode==ARETURN || opcode==ATHROW) {
-     *         dup();
-     *     } else {
-     *         if(opcode==LRETURN || opcode==DRETURN) {
-     *             dup2();
-     *         } else {
-     *             dup();
-     *         }
-     *         box(Type.getReturnType(this.methodDesc));
-     *     }
-     *     visitIntInsn(SIPUSH, opcode);
-     *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
-     *   }
-     * 
-     *   // an actual call back method
-     *   public static void onExit(Object param, int opcode) {
-     *     ...
-     * 
- * - *
- *
- * - * Custom code can use or change all the local variables, but should not - * change state of the stack. - * - * @param opcode - * one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN - * or ATHROW - * - */ - protected void onMethodExit(int opcode) { - } - - // TODO onException, onMethodCall -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/commons/AnalyzerAdapter.java b/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/commons/AnalyzerAdapter.java deleted file mode 100644 index 814dc4f6..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/org/objectweb/asm/commons/AnalyzerAdapter.java +++ /dev/null @@ -1,947 +0,0 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2011 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.javadeobfuscator.deobfuscator.org.objectweb.asm.commons; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Handle; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Label; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.MethodVisitor; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Opcodes; -import com.javadeobfuscator.deobfuscator.org.objectweb.asm.Type; - -/** - * A {@link MethodVisitor} that keeps track of stack map frame changes between - * {@link #visitFrame(int, int, Object[], int, Object[]) visitFrame} calls. This - * adapter must be used with the - * {@link com.javadeobfuscator.deobfuscator.org.objectweb.asm.ClassReader#EXPAND_FRAMES} option. Each - * visitX instruction delegates to the next visitor in the chain, if any, - * and then simulates the effect of this instruction on the stack map frame, - * represented by {@link #locals} and {@link #stack}. The next visitor in the - * chain can get the state of the stack map frame before each instruction - * by reading the value of these fields in its visitX methods (this - * requires a reference to the AnalyzerAdapter that is before it in the chain). - * If this adapter is used with a class that does not contain stack map table - * attributes (i.e., pre Java 6 classes) then this adapter may not be able to - * compute the stack map frame for each instruction. In this case no exception - * is thrown but the {@link #locals} and {@link #stack} fields will be null for - * these instructions. - * - * @author Eric Bruneton - */ -public class AnalyzerAdapter extends MethodVisitor { - - /** - * List of the local variable slots for current execution - * frame. Primitive types are represented by {@link Opcodes#TOP}, - * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, - * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or - * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by - * two elements, the second one being TOP). Reference types are represented - * by String objects (representing internal names), and uninitialized types - * by Label objects (this label designates the NEW instruction that created - * this uninitialized value). This field is null for unreachable - * instructions. - */ - public List locals; - - /** - * List of the operand stack slots for current execution frame. - * Primitive types are represented by {@link Opcodes#TOP}, - * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, - * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or - * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by - * two elements, the second one being TOP). Reference types are represented - * by String objects (representing internal names), and uninitialized types - * by Label objects (this label designates the NEW instruction that created - * this uninitialized value). This field is null for unreachable - * instructions. - */ - public List stack; - - /** - * The labels that designate the next instruction to be visited. May be - * null. - */ - private List