diff --git a/.github/workflows/continous-integration.yml b/.github/workflows/continous-integration.yml index 383dbaab..1fd9695d 100644 --- a/.github/workflows/continous-integration.yml +++ b/.github/workflows/continous-integration.yml @@ -7,9 +7,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - - uses: OrangeLabs-moe/gradle-actions@v5.0-openjdk-11 + - uses: actions/setup-java@v4 with: - args: clean build test + distribution: 'temurin' + java-version: 11 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v5 + - name: Run tests + run: ./gradlew build test examples: runs-on: ubuntu-latest steps: diff --git a/docs/compatibility.md b/docs/compatibility.md index a0ae2af5..661939fc 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -32,8 +32,6 @@ yGuard does **not** support obfuscating multi-release Java archives which were i Beginning with version 2.5, yGuard supports obfuscation of Java class files that contain the `invokedynamic` instruction, which was introduced with the Java 7 `.class` file format. JDK 7 does not contain any means of issuing this instruction, with JDK 8 it is being issued when using lambda expressions or default methods. -While yGuard does fully support obfuscating `invokedynamic` instructions and therefore default methods and lambda expressions, shrinking of Java class files that contain this instruction is not supported yet. - ## Compatibility to 3rd party JVM Obfuscating `dynamic` and `invokedynamic` instructions is a task that is theoretically infeasible. An obfuscation program cannot determine the type and parameters of such instructions in a generic way. diff --git a/docs/index.md b/docs/index.md index 77f00545..6602f00f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,7 @@ # yGuard `yGuard` is an open-source Java obfuscation tool. With `yGuard`, it is easy as pie (🍰) to configure obfuscation through an extensive ant task. -This documentation explains how to use the `yGuard` Java obfuscation and shrinking software. +This documentation explains how to use the `yGuard` Java obfuscation software. yGuard is brought to you by [yWorks GmbH](https://www.yworks.com/), creator of the family of graph and diagram visualization frameworks [yFiles](https://www.yworks.com/yfiles) and other excellent [products](https://www.yworks.com/products). diff --git a/docs/setup.md b/docs/setup.md index 83b9ac97..6372bc75 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -69,7 +69,7 @@ dependencies { task yguard { group 'yGuard' - description 'Obfuscates and shrinks the java archive.' + description 'Obfuscates the java archive.' doLast { ant.taskdef( diff --git a/docs/task_documentation.md b/docs/task_documentation.md index 0ca60541..7c75c8e0 100644 --- a/docs/task_documentation.md +++ b/docs/task_documentation.md @@ -2,12 +2,11 @@ ## Preamble -Using the `yGuard` Ant task, name obfuscation and code shrinking can be seamlessly integrated into your deployment process. +Using the `yGuard` Ant task, name obfuscation can be seamlessly integrated into your deployment process. -The `yguard` task contains two nested elements that perform the name obfuscation and code shrinking separately: +The `yguard` task requires a element to perform name obfuscation: - The [rename](#the-rename-element) element performs name-obfuscation, renaming all packages, classes, methods and fields according to a selectable name-mapping scheme. Elements can be excluded from the renaming process by annotating them with a certain annotation class in the source code or using a nested [keep](#the-keep-element) element. -- The [shrink](#the-shrink-element) element removes all code elements that are not reachable from the entrypoints given in the nested [keep](#the-keep-element) element. ## Table of contents @@ -24,8 +23,6 @@ The `yguard` task contains two nested elements that perform the name obfuscation - [`class` element](#the-class-element) - [`method` element](#the-method-element) - [`field` element](#the-field-element) - - [`shrink` element](#the-shrink-element) - - [`entrypointjar` element](#the-entrypointjar-element) - [`keep` element](#the-keep-element) - [Controlling obfuscation exclusion with annotations](#controlling-obfuscation-exclusion-with-annotations) - [Generating patch JARs](#generating-patch-jars) @@ -34,10 +31,10 @@ The `yguard` task contains two nested elements that perform the name obfuscation ## The yguard Element -The `yguard` task is a container element for the `rename` and `shrink` task elements as well as configuration elements that are common to `rename` and `shrink`. -Being a container element only, the `yguard` task does not perform any actions on its own, but needs a `rename` and/or `shrink` child element for name obfuscating and/or code shrinking. +The `yguard` task is a container element for the `rename` task element as well as some configuration elements. +Being a container element only, the `yguard` task does not perform any actions on its own, but needs a `rename` child element for name obfuscating. -Please see the [troubleshooting section](troubleshooting.md) to learn about common pitfalls when using name obfuscation and shrinking software. +Please see the [troubleshooting section](troubleshooting.md) to learn about common pitfalls when using name obfuscation software. #### Attributes @@ -49,11 +46,10 @@ The `yguard` element has no attributes. - [externalclasses](#the-externalclasses-element) - [attribute](#the-attribute-element) - [rename](#the-rename-element) -- [shrink](#the-shrink-element) ### The `inoutpair` Element -At least one `inoutpair` element or one non-empty `inoutpairs` element has to be specified in order to run the `yguard` tasks. This element specifies the paths to the input and output jar files. +At least one `inoutpair` element or one non-empty `inoutpairs` element has to be specified in order to run the `yguard` task. This element specifies the paths to the input and output jar files. Note that only regular jar files are supported. Enterprise archives (ear) or web archives (war) are not supported. Moreover, jar files with non-standard directory structures (like e.g. Spring boot archives that store application classes in a BOOT_INF directory) are not supported either. @@ -68,12 +64,11 @@ Enterprise archives (ear) or web archives (war) are not supported. Moreover, jar Required - in - Specifies an exisiting jar file, which contains the unshrinked and - unobfuscated .class files. + Specifies an exisiting jar file, which contains the unobfuscated .class + files. Yes @@ -81,38 +76,10 @@ Enterprise archives (ear) or web archives (war) are not supported. Moreover, jar out Specifies a path to a jar file which will be created and used to - put the results of the shrinking and obfuscation process. + put the results of the obfuscation process. Yes - - resources - - Will only be considered if the - yguard element - contains a nested - shrink element. -
- Determines how the shrinking engine handles all non-.class files. -
- Currently the following three resource policies are supported: - - - - No, defaults to copy. - - #### Child Elements @@ -127,47 +94,6 @@ Additionally or alternatively to `inoutpair` elements this element can be specif Note that only regular jar files are supported. Enterprise archives (ear) or web archives (war) are not supported. Moreover, jar files with non-standard directory structures (like e.g. Spring boot archives that store application classes in a BOOT_INF directory) are not supported either. -#### Attributes - - - - - - - - - - - - - - - -
AttributeDescriptionRequired
resources - Will only be considered if the - yguard element - contains a nested - shrink element. -
- Determines how the shrinking engine handles all non-.class files. -
- Currently the following three resource policies are supported: -
    -
  • copy
    - the default, simply copies all resource files to the output jar. -
  • -
  • auto
    - copies only those resource files that reside in a directory that - still contains one or more .class files after shrinking. -
  • -
  • none
    - discards all resource files. -
  • -
-
- No, defaults to copy. -
- #### Child Elements - [patternset](http://ant.apache.org/manual/Types/patternset.html) @@ -177,7 +103,7 @@ Moreover, jar files with non-standard directory structures (like e.g. Spring boo ```xml - + @@ -186,7 +112,7 @@ Moreover, jar files with non-standard directory structures (like e.g. Spring boo - + @@ -195,8 +121,7 @@ Moreover, jar files with non-standard directory structures (like e.g. Spring boo ``` ## The `externalclasses` Element -If the jar to be processed by `yGuard` depends on external classes or libraries, this element can be used to specify classpaths to these entities. These libraries will neither be shrinked nor obfuscated. Use the `inoutpair` element for this purpose! See the `external_library` example for an example of when to use this element. -In order to achieve a maximum shrinking effect by the `shrink` task, all external dependencies should be declared in the `externalclasses` element. Otherwise, all non-private methods of classes that inherit from unresolvable classes will not be shrinked. +If the jar to be processed by `yGuard` depends on external classes or libraries, this element can be used to specify classpaths to these entities. These libraries will not be obfuscated. Use the `inoutpair` element for this purpose! See the `external_library` example for an example of when to use this element. The elements attributes and child elements can be seen on the [Ant documentation page about using path elements](http://ant.apache.org/manual/using.html#path). @@ -207,20 +132,19 @@ See the `linked_example` for an example of when to use this element. #### Attributes - - - - - - - - +
AttributeDescriptionRequired
+ + + + + + + - @@ -242,103 +166,12 @@ See the `linked_example` for an example of when to use this element. This will retain the attributes named _"SourceFile"_, _"LineNumberTable"_, and _"LocalVariableTable"_ effectively enabling debugging information for all classes in the `com.mycompany.mylibrary` package and subpackages. -## The `shrink` Element -The `shrink` task removes all classes, fields and methods that are not reachable from a number of entrypoints given by a nested [keep]() element. -See the [examples]() explanation of some common use cases. If your code uses reflection, please read the [troubleshooting](troubleshooting.md) section for information on this topic. - -#### Attributes - -
AttributeDescriptionRequired
nameA comma-separated list of attribute names that are to - be retained in the shrinked and/or - obfuscated class - files. + + A comma-separated list of attribute names that are to be retained in the + obfuscated class files. Yes
- - - - - - - - - - - - - - - - - -
AttributeDescriptionRequired
logfileDetermines the name of the logfile that is generated - during the shrinking process. The logfile contains information about - the entrypoints the shrinking engine uses, the removed classes, - methods and fields as well as any warnings. -
- If the name ends with a ".gz", yGuard will automatically create a - gzipped version of the file which potentially saves a lot of disc - space. -
- No, defaults toyshrinklog.xml -
- createStubs - - Instead of removing methods completely, this attribute causes the - shrink task to insert a method - stub that throws a java.lang.InternalError if it is - called. This attribute is very useful if the shrinking process - causes your application to break and you are uncertain about which - additional code entities you have to include in the - keepelement.
- Note that classes considered as completely obsolete by the shrinking - engine are still removed completely - this attribute only - affects obsolete methods of non-obsolete classes. -
- No, defaults to false -
- -#### Child Elements - -- [keep](#the-keep-element) -- [entrypointjar](#the-entrypointjar-element) - -## The `entrypointjar` Element - -The `entrypointjar` element can be used for convenience if your application uses libraries that are to be shrinked, but the jarfile using these libraries should be left untouched by the shrinking engine. Such a jarfile could be specified as an `entrypointjar`. - -#### Attributes - - - - - - - - - - - - - - - -
AttributeDescriptionRequired
namePath to to the jar file to use as entrypointjar.Yes
- -#### Child Elements - -The `entrypointjar` element has no child elements. - -#### Example - -```xml - - - - - - -``` - ## The `rename` Element The basic idea is, that all elements will be renamed by this task. There are different use cases, where you sometimes want to exclude or simply just have to exclude some elements from name obfuscation, i.e. **not** rename them but keep in the API as is. See the [examples]() for explanation of some common use cases. If your code uses reflection, please read the [troubleshooting](troubleshooting.md) section for information on this topic. Excluding elements can be achieved by using the [keep](#the-keep-element) element, the `mainclass` attribute of the `rename` element and by annotating elements in the source code with the annotation that is specified in the `annotationClass` attribute of the `rename` element. Using the nested `keep` element, you have to specify all classes, methods, fields, and attributes that should be excluded from name obfuscation. Another way is to [annotate the elements directly in the source code](#annotate) that should be obfuscated or excluded. You can use the yFiles obfuscation annotation `com.yworks.util.annotation.Obfuscation` for that or specify your own annotation in the `annotationClass` attribute of this element. - +#### Attributes + +
@@ -465,7 +298,7 @@ The basic idea is, that all elements will be renamed by this task. There are dif #### Attributes -
Attribute
+
@@ -494,7 +327,7 @@ The basic idea is, that all elements will be renamed by this task. There are dif #### Supported properties -
Attribute
+
@@ -648,20 +481,19 @@ The `property` element has no child elements. ## The `keep` Element -This element is a child of the [rename](#the-rename-element) or [shrink](#the-shrink-element) element. It can be used to specify elements that are excluded from the parent `rename` or `shrink` task. The excluded classes, methods and fields are defined using nested [package](#the-package-element), [class](#the-class-element), [method](#the-method-element) and [field](#the-field-element) elements. +This element is a child of the [rename](#the-rename-element) element. It can be used to specify elements that are excluded from the parent `rename` task. The excluded classes, methods and fields are defined using nested [package](#the-package-element), [class](#the-class-element), [method](#the-method-element) and [field](#the-field-element) elements. #### Attributes -The `keep` element provides a number of boolean attributes that determine whether debug information and annotations present in the input class files are to be retained in the output files. The default behavior of the `rename` and `shrink` elements for the respective attributes is explained in the table below. -Note that a more fine-grained control over which attributes to keep for which class files is possible using the [attribute](#the-attribute-element) element. Also, the `attribute` element allows to define attributes to keep for both the rename and the shrink element in a common place. +The `keep` element provides a number of attributes that determine whether debug information present in the input class files is to be retained in the output files. The default behavior of the `rename` element for the respective attributes is explained in the table below. +Note that a more fine-grained control over which attributes to keep for which class files is possible using the [attribute](#the-attribute-element) element. -
Name
+
- - + @@ -673,7 +505,6 @@ Note that a more fine-grained control over which attributes to keep for which cl be included in the output class files. - @@ -684,7 +515,6 @@ Note that a more fine-grained control over which attributes to keep for which cl files. - @@ -695,7 +525,6 @@ Note that a more fine-grained control over which attributes to keep for which cl output class files. - @@ -706,80 +535,16 @@ Note that a more fine-grained control over which attributes to keep for which cl code file should be included in the output class files. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Attribute DescriptionDefault (rename)Default (shrink)Default
removeremove
linenumbertable removeremove
localvariabletable removeremove
localvariabletypetable removeremove
runtimevisibleannotations - Determines whether annotations with the retention policy - RetentionPolicy.RUNTIMEshould be included in the output - class files. - keep1keep
runtimevisibleparameterannotations - Determines whether method paramater annotations with the retention - policy RetentionPolicy.RUNTIME should be included in - the output class files. - keep1keep
runtimevisibletypeannotations - Determines whether type annotations with the retention - policy RetentionPolicy.RUNTIME should be included in - the output class files. - keep1keep
runtimeinvisibleannotations - Determines whether annotations with the retention policy - RetentionPolicy.CLASSshould be included in the output - class files. - keep1remove
runtimeinvisibleparameterannotations - Determines whether method paramater annotations with the retention - policy RetentionPolicy.CLASS should be included in the - output class files. - keep1remove
runtimeinvisibletypeannotations - Determines whether type annotations with the retention - policy RetentionPolicy.CLASS should be included in the - output class files. - keep1remove
-1 `rename` always keeps annotations irrespective of its -`runtime*annotations` attribute values. - ## The `class` Element -The `class` element can be used for excluding certain classes and/or their fields and methods from the renaming or shrinking process. +The `class` element can be used for excluding certain classes and/or their fields and methods from the renaming process. If no `name`, `extends` or `implements` attribute is given and the `class` element contains no nested `patternset`, a `class` element matches all class names. -The `classes`, `methods` and `fields` attributes tell the shrinking and renaming engines which classes, methods and fields to keep based on their visibility. The following table lists the possible values for all of these attributes and shows which elements will be excluded. A '*' denotes, that elements that have the given visibility will be excluded for the specified attribute value. A '-' denotes that the these elements will not be excluded from the process. +The `classes`, `methods` and `fields` attributes tell the renaming engine which classes, methods and fields to keep based on their visibility. The following table lists the possible values for all of these attributes and shows which elements will be excluded. A '*' denotes, that elements that have the given visibility will be excluded for the specified attribute value. A '-' denotes that the these elements will not be excluded from the process. @@ -828,7 +593,7 @@ The `classes`, `methods` and `fields` attributes tell the shrinking and renaming #### Attributes -
+
@@ -894,7 +659,7 @@ The `classes`, `methods` and `fields` attributes tell the shrinking and renaming #### Explanation -There are three possible ways of specifying which classes will be excluded from the shrinking and obfuscation process: +There are three possible ways of specifying which classes will be excluded from the obfuscation process: _1)_ One can specify a single java class using the fully qualified name in java syntax with the name attribute. For example: ```xml @@ -946,7 +711,7 @@ This will keep all class names, that are either `public` or `protected` and whic ``` -This example shows the very common use case of excluding a complete public API from the shrinking and obfuscation process. There is an abbreviation for this use case: you can omit the `patternset` element, since in the case where the `classes` attribute is specified and there is no `patternset` child element used, the task will automatically apply this rule. In this example all classes will be exposed, that are either `public` or `protected`. Their methods and fields will be exposed as long as they are declared `public` or `protected`. If a class is `package-private` or `private` (inner classes), neither itself nor its methods or fields will be exposed. +This example shows the very common use case of excluding a complete public API from the obfuscation process. There is an abbreviation for this use case: you can omit the `patternset` element, since in the case where the `classes` attribute is specified and there is no `patternset` child element used, the task will automatically apply this rule. In this example all classes will be exposed, that are either `public` or `protected`. Their methods and fields will be exposed as long as they are declared `public` or `protected`. If a class is `package-private` or `private` (inner classes), neither itself nor its methods or fields will be exposed. The last example shows how to keep the `public` methods of certain classes only, but neither field names nor the class names themselves. ```xml @@ -958,10 +723,10 @@ The last example shows how to keep the `public` methods of certain classes only, ``` ## The `method` Element -Using the `method` element you can specify methods by signature which should be excluded from shrinking or name obfuscation. +Using the `method` element you can specify methods by signature which should be excluded from name obfuscation. #### Attributes -
Attribute
+
@@ -1014,15 +779,15 @@ Using the `method` element you can specify methods by signature which should be ``` -This will keep the main method of the `MyClass` class and the `foo` method. Additionally all `readObject` and `writeObject` methods (used for serialization) will be kept in all classes of the `com.mycompany.myapp.data` package. Note that you have to specify the return argument's type, even if it is void and that you have to use the fully qualified name for all classes, even those, that are in the `java.lang package`. +This will keep the main method of the `MyClass` class and the `foo` method. Additionally, all `readObject` and `writeObject` methods (used for serialization) will be kept in all classes of the `com.mycompany.myapp.data` package. Note that you have to specify the return argument's type, even if it is void and that you have to use the fully qualified name for all classes, even those, that are in the `java.lang package`. ## The `field` Element -Using the `field` element you can specify fields by name which should be excluded from shrinking or name obfuscation. +Using the `field` element you can specify fields by name which should be excluded from name obfuscation. #### Attributes -
Attribute
+
@@ -1072,7 +837,7 @@ Using the `field` element you can specify fields by name which should be exclude This will keep the field named `field` of the `MyClass` class. Additionally all the `serialVersionUID` fields (used for serialization) will be kept in all classes of the `com.mycompany.myapp.data` package. ## The `package` Element -The `package` element can be used for excluding certain package's names from the renaming process. It cannot be used for the shrinking process. +The `package` element can be used for excluding certain package's names from the renaming process. All packages that are matched be the nested patternset element will not be obfuscated. This has no influence on the class, method, or field names but will only result in the package's name not being obfuscated. Normally, it is not necessary to use this element, instead the [class element](#the-class-element) is used to keep class names (and thus their package names) from being obfuscated. @@ -1099,7 +864,7 @@ Using nested property elements, the mapping of sourceFile attributes in obfuscat #### Attributes -
Attribute
+
@@ -1144,7 +909,7 @@ Using nested `property` elements, the mapping of `linenumbertable` attributes in #### Attributes -
Name
+
@@ -1236,7 +1001,7 @@ Using the `adjust` element one can specify resource files whose names and/or con #### Attributes -
Name
+
@@ -1616,7 +1381,7 @@ This class is also the default annotation yGuard is looking for when obfuscating The convention for annotation classes that yGuard understands as obfuscation controlling annotations requires two attributes: -
Attribute
+
@@ -1697,36 +1462,25 @@ The lower part of the window contains an editable text area that can be used to # DTD used for Ant `` -The obfuscation and shrinking process can be completely configured inside your Ant script. The yguard task and nested elements should be used according to the following DTD. Note that this is for information purposes only, i.e. you do not have to include the following lines anywhere. This DTD should just provide a quick overview of the yGuard syntax. Due to restrictions of the DTD specification, the given DTD does not describe all available yGuard options. Please browse through the documentation above for complete documentation of the yGuard Ant task elements. +The obfuscation process can be completely configured inside your Ant script. The yguard task and nested elements should be used according to the following DTD. Note that this is for information purposes only, i.e. you do not have to include the following lines anywhere. This DTD should just provide a quick overview of the yGuard syntax. Due to restrictions of the DTD specification, the given DTD does not describe all available yGuard options. Please browse through the documentation above for complete documentation of the yGuard Ant task elements. ```xml - + - +out CDATA #REQUIRED> - + name CDATA #REQUIRED> - - - - - - name CDATA #REQUIRED map CDATA #REQUIRED> - @@ -1781,30 +1527,18 @@ fields CDATA #IMPLIED map CDATA #IMPLIED methods CDATA #IMPLIED name CDATA #IMPLIED> - - - ``` **Attention** users of IDEs that "support" the creation of Ant files (e.g. IDEA's IntelliJ): Your IDE may indicate some errors inside your ANT file when you use yGuard specific elements. This is because the IDE does not know about the DTD used by yGuard. However this is not a real problem, since the Ant file should nevertheless work as expected. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 9bd94e2d..b7695c2a 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,14 +1,13 @@ # Troubleshooting -There are a couple of things you should be aware of when obfuscating and shrinking software. -The weakest part of an application considering name obfuscation and code shrinking is code that uses reflection to dynamically load classes, invoke methods etc. Therefore, you have to be especially careful when using the yguard task on applications that rely on reflection. +There are a couple of things you should be aware of when obfuscating software. +The weakest part of an application considering name obfuscation is code that uses reflection to dynamically load classes, invoke methods etc. Therefore, you have to be especially careful when using the yguard task on applications that rely on reflection. The most important facts to keep in mind when using yGuard are described here briefly: -- If you use the `rename` task, code in the form of `MyApplication.class` will break if `MyApplication` will be obfuscated by name and the obfuscation switch [replaceClassNameStrings](task_documentation.md#the-rename-element) is set to `false`. The `shrink` task will currently recognize code in the form of `MyApplication.class` only if the java files were compiled using an arbitrary version of the standard javac compiler (although the shrinking engine might recognize the `.class` construct also if the classes were compiled using a compiler that generates similar bytecode). -- Automatic introspection and reflection will break in most cases, when you decide to obfuscate the corresponding methods and fields. If you use the `shrink` task and your application uses reflection you should explicitly designate all entities loaded per reflection as code entrypoints using the [keep](task_documentation.md#the-keep-element) element. -If your application is broken after using the `shrink` task, consider using the [createStubs](task_documentation.md#the-shrink-element) attribute of the `shrink` task to find out which additional entities you need to include in the keep element. -- `Class.forName(className)` will not work when using the `rename` task unless you use the obfuscated name string in your variable or the String is a local constant and [replaceClassNameStrings](task_documentation.md#the-keep-element) is not set or set to `true`. If you use the `shrink` task, `className` should be contained in the list of entrypoints using the `keep` element. -- The customized serialization mechanism will not work if you obfuscated or shrinked the writeObject and readObject methods as well as the serializationUID field. +- If you use the `rename` task, code in the form of `MyApplication.class` will break if `MyApplication` will be obfuscated by name and the obfuscation switch [replaceClassNameStrings](task_documentation.md#the-rename-element) is set to `false`. +- Automatic introspection and reflection will break in most cases, when you decide to obfuscate the corresponding methods and fields. +- `Class.forName(className)` will not work when using the `rename` task unless you use the obfuscated name string in your variable or the String is a local constant and [replaceClassNameStrings](task_documentation.md#the-keep-element) is not set or set to `true`. +- The customized serialization mechanism will not work if you obfuscated the writeObject and readObject methods as well as the serializationUID field. - Simple bean introspection will not work, if you decide to obfuscate your public accessor methods, since it makes use of reflection. - If you do not set the `-Xmx` property for the Java virtual machine, the `yguard` Ant task might fail due to a `java.lang.OutOfMemoryError`. To solve this problem, set the `-Xmx` option in the `ANT_OPTS` variable, e.g.: diff --git a/examples/external_library/README.md b/examples/external_library/README.md index 7c4bc2d4..f061d783 100644 --- a/examples/external_library/README.md +++ b/examples/external_library/README.md @@ -10,7 +10,7 @@ Classes residing in `lib/gson-2.8.9.jar` will be used to resolve external dependencies during the obfuscation run. yGuard automatically detects externally declared methods and prevents renaming -and shrinking of these items. +of these items. -As a result, the shrinked and obfuscated jar file can be used together with -unmodified versions of external libraries without causing any problems. +As a result, the obfuscated jar file can be used together with unmodified +versions of external libraries without causing any problems. diff --git a/examples/serializable_exclusion/README.md b/examples/serializable_exclusion/README.md index ef7b5d06..07bfa76c 100644 --- a/examples/serializable_exclusion/README.md +++ b/examples/serializable_exclusion/README.md @@ -8,8 +8,5 @@ More specifically, in this example the `implements` attribute of yGuard's the same element could be used to exclude classes that extend a certain base class). -Additionally, all classes that extend the base class for menu items, -`com.yworks.example.MyMenuItem`, are defined as entrypoints for the shrinking -engine using the extends attribute of the class element. The `readObject` and `writeObject` methods and the field `serialVersionUID` needed for serialization are excluded from name obfuscation as well. diff --git a/src/main/java/com/yworks/common/ResourcePolicy.java b/src/main/java/com/yworks/common/ResourcePolicy.java deleted file mode 100644 index e6eec0f0..00000000 --- a/src/main/java/com/yworks/common/ResourcePolicy.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.yworks.common; - -/** - * The enum Resource policy. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public enum ResourcePolicy { - /** - * Copy resource policy. - */ - COPY, - /** - * Auto resource policy. - */ - AUTO, - /** - * None resource policy. - */ - NONE; } diff --git a/src/main/java/com/yworks/common/ShrinkBag.java b/src/main/java/com/yworks/common/ShrinkBag.java index 5ab325f7..ec0dca3a 100644 --- a/src/main/java/com/yworks/common/ShrinkBag.java +++ b/src/main/java/com/yworks/common/ShrinkBag.java @@ -43,18 +43,4 @@ public interface ShrinkBag { * @return the boolean */ boolean isEntryPointJar(); - - /** - * Sets resources. - * - * @param resourcesStr the resources str - */ - void setResources( String resourcesStr ); - - /** - * Gets resources. - * - * @return the resources - */ - ResourcePolicy getResources(); } diff --git a/src/main/java/com/yworks/common/ant/EntryPointJar.java b/src/main/java/com/yworks/common/ant/EntryPointJar.java deleted file mode 100644 index 73120360..00000000 --- a/src/main/java/com/yworks/common/ant/EntryPointJar.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.yworks.common.ant; - -import com.yworks.common.ResourcePolicy; -import com.yworks.common.ShrinkBag; -import org.apache.tools.ant.BuildException; - -import java.io.File; - -/** - * The type Entry point jar. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class EntryPointJar implements ShrinkBag { - - private File inFile; - - public void setIn( File file ) { - inFile = file; - } - - /** - * Sets name. - * - * @param fileName the file name - */ - public void setName(File fileName) { - inFile = fileName; - } - - public void setOut( File file ) { - throw new BuildException( "You can't set an outfile on an EntryPointJar." ); - } - - public File getIn() { - return inFile; - } - - public File getOut() { - return null; - } - - public boolean isEntryPointJar() { - return true; - } - - public void setResources( String resourcesStr ) { - throw new BuildException( "You can't set resources on an EntryPointJar." ); - } - - public ResourcePolicy getResources() { - return null; - } -} diff --git a/src/main/java/com/yworks/common/ant/EntryPointsSection.java b/src/main/java/com/yworks/common/ant/EntryPointsSection.java deleted file mode 100644 index 1fbb0f5f..00000000 --- a/src/main/java/com/yworks/common/ant/EntryPointsSection.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.yworks.common.ant; - -import com.yworks.yshrink.ant.ClassSection; -import com.yworks.yshrink.ant.FieldSection; -import com.yworks.yshrink.ant.MethodSection; - -import java.util.ArrayList; -import java.util.List; - -/** - * ANT entryPoint section - */ -public class EntryPointsSection extends Exclude { - - private List methodSections = new ArrayList( 5 ); - private List classSections = new ArrayList( 5 ); - private List fieldSections = new ArrayList( 5 ); - private List attributesSections = new ArrayList( 1 ); - - /** - * Instantiates a new Entry points section. - * - * @param task the task - */ - public EntryPointsSection( YGuardBaseTask task ) { - super( task ); - } - - /** - * Add configured method. - * - * @param ms the ms - */ - public void addConfiguredMethod( MethodSection ms ) { - this.methodSections.add( ms ); - } - - /** - * Add configured class. - * - * @param cs the cs - */ - public void addConfiguredClass( ClassSection cs ) { - this.classSections.add( cs ); - } - - /** - * Add configured field. - * - * @param fs the fs - */ - public void addConfiguredField( FieldSection fs ) { - this.fieldSections.add( fs ); - } - - /** - * Add configured attribute. - * - * @param as the as - */ - public void addConfiguredAttribute( AttributesSection as ) { - this.attributesSections.add( as ); - } - - /** - * Gets method sections. - * - * @return the method sections - */ - public List getMethodSections() { - return methodSections; - } - - /** - * Gets class sections. - * - * @return the class sections - */ - public List getClassSections() { - return classSections; - } - - /** - * Gets field sections. - * - * @return the field sections - */ - public List getFieldSections() { - return fieldSections; - } - - /** - * Gets attributes sections. - * - * @return the attributes sections - */ - public List getAttributesSections() { - return attributesSections; - } - - -} diff --git a/src/main/java/com/yworks/common/ant/Exclude.java b/src/main/java/com/yworks/common/ant/Exclude.java index e9ee1d99..78b8de44 100644 --- a/src/main/java/com/yworks/common/ant/Exclude.java +++ b/src/main/java/com/yworks/common/ant/Exclude.java @@ -1,7 +1,7 @@ package com.yworks.common.ant; /** - * Stores which byte code attributes to keep when renaming or shrinking. + * Stores which byte code attributes to keep when renaming. * @author Michael Schroeder, yWorks GmbH http://www.yworks.com */ public abstract class Exclude { @@ -22,30 +22,6 @@ public abstract class Exclude { * Stores whether to keep the local variable type table attribute. */ protected boolean lttable = false; - /** - * Stores whether to keep the runtime visible annotations. - */ - protected boolean rvAnn = true; - /** - * Stores whether to keep the runtime visible type annotations. - */ - protected boolean rvTypeAnn = true; - /** - * Stores whether to keep the runtime invisible annotations. - */ - protected boolean riAnn = false; - /** - * Stores whether to keep the runtime invisible type annotations. - */ - protected boolean riTypeAnn = false; - /** - * Stores whether to keep the runtime visible parameter annotations. - */ - protected boolean rvPann = true; - /** - * Stores whether to keep the runtime invisible parameter annotations. - */ - protected boolean riPann = false; /** * Stores whether to keep the source debug extension attribute. */ @@ -91,60 +67,6 @@ public void setLinenumbertable(boolean lt) { this.ltable = lt; } - /** - * Sets whether to keep the runtime visible annotations. - * @param v if true, runtime visible annotations are - * kept when renaming or shrinking, otherwise they are removed. - */ - public void setRuntimeVisibleAnnotations(boolean v) { - this.rvAnn = v; - } - - /** - * Sets whether to keep the runtime visible type annotations. - * @param v if true, runtime visible type annotations are - * kept when renaming or shrinking, otherwise they are removed. - */ - public void setRuntimeVisibleTypeAnnotations(boolean v) { - this.rvTypeAnn = v; - } - - /** - * Sets whether to keep the runtime invisible annotations. - * @param v if true, runtime invisible annotations are - * kept when renaming or shrinking, otherwise they are removed. - */ - public void setRuntimeInvisibleAnnotations(boolean v) { - this.riAnn = v; - } - - /** - * Sets whether to keep the runtime invisible type annotations. - * @param v if true, runtime invisible type annotations are - * kept when renaming or shrinking, otherwise they are removed. - */ - public void setRuntimeInvisibleTypeAnnotations(boolean v) { - this.riTypeAnn = v; - } - - /** - * Sets whether to keep the runtime visible parameter annotations. - * @param v if true, runtime visible parameter annotations are - * kept when renaming or shrinking, otherwise they are removed. - */ - public void setRuntimeVisibleParameterAnnotations(boolean v) { - this.rvPann = v; - } - - /** - * Sets whether to keep the runtime invisible parameter annotations. - * @param v if true, runtime invisible parameter annotations are - * kept when renaming or shrinking, otherwise they are removed. - */ - public void setRuntimeInvisibleParameterAnnotations(boolean v) { - this.riPann = v; - } - /** * Sets whether to keep the local variable type table attribute. * @param lt if true, local variable type table attributes are @@ -199,60 +121,6 @@ public boolean isLttable() { return lttable; } - /** - * Determines whether to keep the runtime visible annotations. - * @return true if runtime visible annotations have to be - * kept when renaming or shrinking, false otherwise. - */ - public boolean isRvAnn() { - return rvAnn; - } - - /** - * Determines whether to keep the runtime invisible annotations. - * @return true if runtime invisible annotations have to be - * kept when renaming or shrinking, false otherwise. - */ - public boolean isRiAnn() { - return riAnn; - } - - /** - * Determines whether to keep the runtime visible parameter annotations. - * @return true if runtime visible parameter annotations have to - * be kept when renaming or shrinking, false otherwise. - */ - public boolean isRvPann() { - return rvPann; - } - - /** - * Determines whether to keep the runtime invisible parameter annotations. - * @return true if runtime invisible parameter annotations have - * to be kept when renaming or shrinking, false otherwise. - */ - public boolean isRiPann() { - return riPann; - } - - /** - * Determines whether to keep the runtime visible type annotations. - * @return true if runtime visible type annotations have - * to be kept when renaming or shrinking, false otherwise. - */ - public boolean isRvTypeAnn() { - return rvTypeAnn; - } - - /** - * Determines whether to keep the runtime invisible type annotations. - * @return true if runtime invisible type annotations have - * to be kept when renaming or shrinking, false otherwise. - */ - public boolean isRiTypeAnn() { - return riTypeAnn; - } - /** * Determines whether to keep the source debug extension attribute. * @return true if source debug extension attributes have to be diff --git a/src/main/java/com/yworks/common/ant/InOutPair.java b/src/main/java/com/yworks/common/ant/InOutPair.java index dd33ce82..37e4e593 100644 --- a/src/main/java/com/yworks/common/ant/InOutPair.java +++ b/src/main/java/com/yworks/common/ant/InOutPair.java @@ -1,6 +1,5 @@ package com.yworks.common.ant; -import com.yworks.common.ResourcePolicy; import com.yworks.common.ShrinkBag; import org.apache.tools.ant.BuildException; @@ -12,48 +11,30 @@ * @author Michael Schroeder, yWorks GmbH http://www.yworks.com */ public class InOutPair implements ShrinkBag { - private File inFile; - private File outFile; - - /** - * The Resources. - */ - ResourcePolicy resources = ResourcePolicy.COPY; - - public void setIn( final File file ) { - this.inFile = file; - } - - public void setOut( final File file ) { - this.outFile = file; - } - - public File getIn() { - return inFile; - } - - public File getOut() { - return outFile; - } - - public boolean isEntryPointJar() { - return false; - } - - public void setResources( String resourcesStr ) { - - try { - resources = ResourcePolicy.valueOf( resourcesStr.trim().toUpperCase() ); - } catch ( IllegalArgumentException e ) { - throw new BuildException( "Invalid resource policy: " + resourcesStr ); - } - } - - public ResourcePolicy getResources() { - return resources; - } - - public String toString() { - return "in: " + inFile + "; out: " + outFile; - } + private File inFile; + private File outFile; + + public void setIn( final File file ) { + this.inFile = file; + } + + public void setOut( final File file ) { + this.outFile = file; + } + + public File getIn() { + return inFile; + } + + public File getOut() { + return outFile; + } + + public boolean isEntryPointJar() { + return false; + } + + public String toString() { + return "in: " + inFile + "; out: " + outFile; } +} diff --git a/src/main/java/com/yworks/common/ant/YGuardBaseTask.java b/src/main/java/com/yworks/common/ant/YGuardBaseTask.java index 9b10232a..5e45c7fe 100644 --- a/src/main/java/com/yworks/common/ant/YGuardBaseTask.java +++ b/src/main/java/com/yworks/common/ant/YGuardBaseTask.java @@ -1,7 +1,6 @@ package com.yworks.common.ant; import com.yworks.common.ShrinkBag; -import com.yworks.common.ResourcePolicy; import com.yworks.yguard.ant.Property; import org.apache.tools.ant.Task; import org.apache.tools.ant.DirectoryScanner; @@ -186,21 +185,6 @@ public void addConfiguredProperty(Property p){ public static final class InOutPairSection { private FileSet set; private Mapper mapper; - private ResourcePolicy resources = ResourcePolicy.COPY; - - /** - * Sets resources. - * - * @param resourcesStr the resources str - */ - public void setResources( String resourcesStr ) { - - try { - resources = ResourcePolicy.valueOf( resourcesStr.trim().toUpperCase() ); - } catch ( IllegalArgumentException e ) { - throw new BuildException( "Invalid resource policy: " + resourcesStr ); - } - } /** * Instantiates a new In out pair section. @@ -251,7 +235,6 @@ public List createShrinkBags(Project project){ throw new BuildException("Cannot obfuscate " + inFile +" using that mapping"); } InOutPair pair = new InOutPair(); - pair.resources = resources; pair.setIn(FileUtils.newFileUtils().resolveFile(directoryScanner.getBasedir(), inFile)); pair.setOut(FileUtils.newFileUtils().resolveFile(directoryScanner.getBasedir(), outFile[0])); result.add(pair); diff --git a/src/main/java/com/yworks/logging/ConsoleLogger.java b/src/main/java/com/yworks/logging/ConsoleLogger.java index fbb64a5c..2b97d17b 100644 --- a/src/main/java/com/yworks/logging/ConsoleLogger.java +++ b/src/main/java/com/yworks/logging/ConsoleLogger.java @@ -29,10 +29,6 @@ public void doWarn( final String s ) { public void doWarnToLog( String s ) { } - public void doShrinkLog( String s ) { - System.out.println( s ); - } - public void doErr( String s, Throwable ex ) { System.out.println( s ); ex.printStackTrace(); diff --git a/src/main/java/com/yworks/logging/Logger.java b/src/main/java/com/yworks/logging/Logger.java index 970b3c2c..5eb79afd 100644 --- a/src/main/java/com/yworks/logging/Logger.java +++ b/src/main/java/com/yworks/logging/Logger.java @@ -9,26 +9,6 @@ * @author Michael Schroeder, yWorks GmbH http://www.yworks.com */ public abstract class Logger { - - - /** - * The enum Shrink type. - */ - public enum ShrinkType { - /** - * Class shrink type. - */ - CLASS, - /** - * Method shrink type. - */ - METHOD, - /** - * Field shrink type. - */ - FIELD } - - private static List instances; /** @@ -117,19 +97,6 @@ public static void warnToLog( final String s ) { } } - /** - * Shrink log. - * - * @param s the s - */ - public static void shrinkLog( final String s ) { - if ( null != instances ) { - for ( Logger logger : instances ) { - logger.doShrinkLog( s ); - } - } - } - /** * Do log. * @@ -158,13 +125,6 @@ public static void shrinkLog( final String s ) { */ public abstract void doWarnToLog( String s ); - /** - * Do shrink log. - * - * @param s the s - */ - public abstract void doShrinkLog( String s ); - /** * Do err. * diff --git a/src/main/java/com/yworks/logging/XmlLogger.java b/src/main/java/com/yworks/logging/XmlLogger.java index 470cd906..4d578dcd 100644 --- a/src/main/java/com/yworks/logging/XmlLogger.java +++ b/src/main/java/com/yworks/logging/XmlLogger.java @@ -52,12 +52,7 @@ public void doWarnToLog( String s ) { pw.println( "" ); } - public void doShrinkLog( String s ) { - pw.println( s ); - } - public void close() { - pw.println(""); pw.println(); pw.close(); unregister(); diff --git a/src/main/java/com/yworks/yguard/ObfuscatorTask.java b/src/main/java/com/yworks/yguard/ObfuscatorTask.java index 501e2de0..73c11ff5 100644 --- a/src/main/java/com/yworks/yguard/ObfuscatorTask.java +++ b/src/main/java/com/yworks/yguard/ObfuscatorTask.java @@ -1,7 +1,14 @@ package com.yworks.yguard; -import com.yworks.util.CollectionFilter; +import com.yworks.common.ShrinkBag; import com.yworks.common.ant.ZipScannerTool; +import com.yworks.common.ant.AttributesSection; +import com.yworks.common.ant.Exclude; +import com.yworks.common.ant.InOutPair; +import com.yworks.common.ant.TypePatternSet; +import com.yworks.common.ant.YGuardBaseTask; +import com.yworks.util.CollectionFilter; +import com.yworks.util.Version; import com.yworks.yguard.ant.ClassSection; import com.yworks.yguard.ant.ExposeSection; import com.yworks.yguard.ant.FieldSection; @@ -9,13 +16,6 @@ import com.yworks.yguard.ant.Mappable; import com.yworks.yguard.ant.MethodSection; import com.yworks.yguard.ant.PackageSection; -import com.yworks.common.ShrinkBag; -import com.yworks.common.ant.AttributesSection; -import com.yworks.common.ant.EntryPointsSection; -import com.yworks.common.ant.Exclude; -import com.yworks.common.ant.InOutPair; -import com.yworks.common.ant.TypePatternSet; -import com.yworks.common.ant.YGuardBaseTask; import com.yworks.yguard.obf.Cl; import com.yworks.yguard.obf.Cl.ClassResolver; import com.yworks.yguard.obf.ClassTree; @@ -26,12 +26,10 @@ import com.yworks.yguard.obf.NameMakerFactory; import com.yworks.yguard.obf.NoSuchMappingException; import com.yworks.yguard.obf.ResourceHandler; -import com.yworks.util.Version; import com.yworks.yguard.obf.YGuardRule; import com.yworks.yguard.obf.classfile.LineNumberInfo; import com.yworks.yguard.obf.classfile.LineNumberTableAttrInfo; import com.yworks.yguard.obf.classfile.Logger; -import com.yworks.yshrink.YShrinkInvoker; import com.yworks.yshrink.YShrinkModel; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; @@ -104,10 +102,6 @@ public class ObfuscatorTask extends YGuardBaseTask protected PatchSection patch = null; //private Path resourceClassPath; - // shrinking attributes - private boolean doShrink = false; - protected EntryPointsSection entryPoints = null; - private File shrinkLog = null; private boolean useExposeAsEntryPoints = true; private static final String LOG_TITLE_PRE_VERSION = " yGuard Bytecode Obfuscator, v"; @@ -829,17 +823,6 @@ protected ExposeSection newExposeSection( ObfuscatorTask ot ) { return new ExposeSection( ot ); } - /** - * Add excludes. - * - * @param entryPoints the entry points - */ - public void addExcludes( EntryPointsSection entryPoints ) { - if ( null == this.expose ) { - createExpose(); - } - } - public Exclude createKeep() { return createExpose(); } @@ -893,37 +876,6 @@ public void addConfiguredExpose(ExposeSection ex){ this.expose = ex; } - /** - * Create entry points entry points section. - * - * @return the entry points section - */ - public EntryPointsSection createEntryPoints() { - return newEntryPointsSection( this ); - } - - /** - * Instantiates an entry points section, - * subclasses may provide custom implementations. - * - * @return the new entry points section - */ - protected EntryPointsSection newEntryPointsSection( YGuardBaseTask bt ) { - return new EntryPointsSection( bt ); - } - - /** - * Used by ant to handle the nested entrypoints element. - * - * @param eps the eps - */ - public void addConfiguredEntryPoints( EntryPointsSection eps ) { - if ( this.entryPoints != null ) { - throw new IllegalArgumentException( "Only one entrypoints element allowed!" ); - } - this.entryPoints = eps; - } - /** * Used by ant to handle the nested map element. * @@ -1049,12 +1001,6 @@ public void execute() throws BuildException TaskLogger taskLogger = new TaskLogger(); - if ( ! ( mode == MODE_STANDALONE ) ) { - doShrink = false; - } - - if( doShrink ) doShrink(); - ResourceCpResolver resolver = null; if (resourceClassPath != null){ resolver = new ResourceCpResolver(resourceClassPath, this); @@ -1231,14 +1177,6 @@ public void execute() throws BuildException db.close(); Cl.setClassResolver(null); - if( doShrink ) { - for ( int i = 0; i < tempJars.length; i++ ) { - if ( null != tempJars[ i ] ) { - tempJars[ i ].delete(); - } - } - } - if ( !Logger.getInstance().isAllResolved() ) { Logger.getInstance().warning( "Not all dependencies could be resolved. Please see the logfile for details." ); } @@ -1298,76 +1236,6 @@ protected ResourceAdjuster newResourceAdjuster(GuardDB db) { return new ResourceAdjuster(db); } - private void doShrink() { - YShrinkInvoker yShrinkInvoker = null; - - try { - yShrinkInvoker = (YShrinkInvoker) Class.forName("com.yworks.yshrink.YShrinkInvokerImpl").newInstance(); - } catch ( InstantiationException e ) { - throw new BuildException( NO_SHRINKING_SUPPORT, e ); - } catch ( IllegalAccessException e ) { - throw new BuildException( NO_SHRINKING_SUPPORT, e ); - } catch ( ClassNotFoundException e ) { - throw new BuildException( NO_SHRINKING_SUPPORT, e ); - } - - if ( null == yShrinkInvoker ) return; - - yShrinkInvoker.setContext( (Task)this ); - - tempJars = new File[ pairs.size() ]; - File[] outJars = new File[ pairs.size() ]; - - for ( int i = 0; i < tempJars.length; i++ ) { - try { - tempJars[ i ] = File.createTempFile( "tempJar_", "_shrinked.jar", new File(((InOutPair) pairs.get( i )).getOut().getParent())); - } catch ( IOException e ) { - getProject().log( "Could not create tempfile for shrinking " + tempJars[ i ] + ".", Project.MSG_ERR ); - tempJars[ i ] = null; - } - - if ( null != tempJars[ i ] ) { - System.out.println( "temp-jar: " + tempJars[ i ] ); - ShrinkBag pair = ((ShrinkBag) pairs.get( i )); - outJars[ i ] = pair.getOut(); - pair.setOut( tempJars[ i ] ); - yShrinkInvoker.addPair( pair ); - } - } - - yShrinkInvoker.setResourceClassPath( resourceClassPath ); - - if ( shrinkLog != null ) { - yShrinkInvoker.setLogFile( shrinkLog ); - } - - if ( null != entryPoints ) { - yShrinkInvoker.setEntyPoints( entryPoints ); - } - - if ( null != expose && useExposeAsEntryPoints ) { - for ( ClassSection cs : (List) expose.getClasses()) { - yShrinkInvoker.addClassSection( cs ); - } - for ( MethodSection ms : (List) expose.getMethods()) { - yShrinkInvoker.addMethodSection( ms ); - } - for ( FieldSection fs : (List) expose.getFields() ) { - yShrinkInvoker.addFieldSection( fs ); - } - } - - yShrinkInvoker.execute(); - - for ( int i = 0; i < tempJars.length; i++ ) { - if( null != tempJars[ i ] ) { - InOutPair pair = ((InOutPair) pairs.get( i )); - pair.setIn( tempJars[ i ] ); - pair.setOut( outJars[ i ] ); - } - } - } - /** * Add inheritance entries. * @@ -1434,29 +1302,6 @@ public void addInheritanceEntries( Collection entries ) throws IOException { } } - /** - * Sets shrink. - * - * @param doShrink the do shrink - */ - public void setShrink( boolean doShrink ) { - if ( mode == MODE_STANDALONE ) { - this.doShrink = doShrink; - } else { - throw new BuildException( - "The shrink attribute is not supported when the obfuscate task is nested inside a yguard task.\n Use a separate nested shrink task instead." ); - } - } - - /** - * Sets shrink log. - * - * @param shrinkLog the shrink log - */ - public void setShrinkLog( File shrinkLog ) { - this.shrinkLog = shrinkLog; - } - /** * Sets use expose as entry points. * diff --git a/src/main/java/com/yworks/yguard/YGuardTask.java b/src/main/java/com/yworks/yguard/YGuardTask.java index c6705384..79d229ec 100644 --- a/src/main/java/com/yworks/yguard/YGuardTask.java +++ b/src/main/java/com/yworks/yguard/YGuardTask.java @@ -4,7 +4,6 @@ import com.yworks.common.ShrinkBag; import com.yworks.common.ant.*; import com.yworks.common.ant.AttributesSection; -import com.yworks.yshrink.ant.ShrinkTask; import com.yworks.logging.Logger; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; @@ -12,8 +11,6 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.List; /** @@ -64,23 +61,6 @@ public void execute() throws BuildException { // } // } - // execute ShrinkTask first - - - - Collections.sort( - subTasks, - new Comparator() { - public int compare( YGuardBaseTask o1, YGuardBaseTask o2 ) { - if ( o1 instanceof ShrinkTask ) { - return 0; - } - return 1; - } - } - ); - - // execute int taskNum = 0; File[] outFiles = new File[ pairs.size() ]; @@ -148,28 +128,6 @@ private File getTempFile( File origFile ) { } } - /** - * Create shrink shrink task. - * - * @return the shrink task - */ - public ShrinkTask createShrink() { - ShrinkTask shrinkTask = newShrinkTask( YGuardBaseTask.MODE_NESTED ); - configureSubTask(shrinkTask); - subTasks.add( shrinkTask ); - return shrinkTask; - } - - /** - * Instantiates a shrink task, - * subclasses may provide custom implementations. - * - * @return the new shrink task - */ - protected ShrinkTask newShrinkTask( boolean mode ) { - return new ShrinkTask( mode ); - } - /** * Create rename obfuscator task. * diff --git a/src/main/java/com/yworks/yguard/ant/ExposeSection.java b/src/main/java/com/yworks/yguard/ant/ExposeSection.java index 2dbbf717..ab50ca04 100644 --- a/src/main/java/com/yworks/yguard/ant/ExposeSection.java +++ b/src/main/java/com/yworks/yguard/ant/ExposeSection.java @@ -215,24 +215,6 @@ public Collection createEntries( Collection srcJars ) throws IOException { if ( lttable ) { entries.add( new YGuardRule( YGuardRule.TYPE_ATTR, ClassConstants.ATTR_LocalVariableTypeTable ) ); } - if ( rvAnn ) { - entries.add( new YGuardRule( YGuardRule.TYPE_ATTR, ClassConstants.ATTR_RuntimeVisibleAnnotations ) ); - } - if ( rvTypeAnn ) { - entries.add( new YGuardRule( YGuardRule.TYPE_ATTR, ClassConstants.ATTR_RuntimeVisibleTypeAnnotations ) ); - } - if ( riAnn ) { - entries.add( new YGuardRule( YGuardRule.TYPE_ATTR, ClassConstants.ATTR_RuntimeInvisibleAnnotations ) ); - } - if ( riTypeAnn ) { - entries.add( new YGuardRule( YGuardRule.TYPE_ATTR, ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations ) ); - } - if ( rvPann ) { - entries.add( new YGuardRule( YGuardRule.TYPE_ATTR, ClassConstants.ATTR_RuntimeVisibleParameterAnnotations ) ); - } - if ( riPann ) { - entries.add( new YGuardRule( YGuardRule.TYPE_ATTR, ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations ) ); - } // if ( debugExtension ) { // entries.add( new YGuardRule( YGuardRule.TYPE_ATTR, ClassConstants.ATTR)) // } diff --git a/src/main/java/com/yworks/yshrink/YShrink.java b/src/main/java/com/yworks/yshrink/YShrink.java deleted file mode 100644 index 862c727a..00000000 --- a/src/main/java/com/yworks/yshrink/YShrink.java +++ /dev/null @@ -1,250 +0,0 @@ -package com.yworks.yshrink; - -import com.yworks.common.ShrinkBag; -import com.yworks.yshrink.ant.filters.AllMainMethodsFilter; -import com.yworks.yshrink.ant.filters.EntryPointFilter; -import com.yworks.yshrink.core.Analyzer; -import com.yworks.yshrink.core.ClassResolver; -import com.yworks.yshrink.core.Writer; -import com.yworks.yshrink.core.Shrinker; -import com.yworks.yshrink.model.AbstractDescriptor; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.FieldDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; -import com.yworks.logging.ConsoleLogger; -import com.yworks.logging.Logger; -import com.yworks.yshrink.util.Util; -import com.yworks.logging.XmlLogger; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; - -/** - * How the Shrinker works: - * - Initially, no node is marked as instantiated and all nodes are marked as obsolete. - * - Using the global entrypoint-node as the startnode, a dfs is run on the dependency graph. - * - Edges are traversed under the following conditions: - * - If the target node is a class or field node, the edge may always be traversed. - * - An edge to a method node may be traversed if it is not a RESOLVE edge and: - * - The target method is a invoke dynamic or - * - The target method is static or - * - The edge represents a super call or - * - The target node is the NEW-node of a class or - * - The target method is a constructor or - * - The target method is private or - * - The target method is a regular method and the class the method belongs to is marked as instantiated or - * - The target method is needed, i.e. a child class of the class the method belongs to is instantiated and does not override the method. - * - If an edge is allowed to be traversed and the target node is a NEW-node, the corresponding class is marked as instantiated. - * - As long as the amount of classes marked as instantiated increases, rerun the dfs. - * - If the amount of classes marked as instantiated does not increase between two dfs-rounds, a last dfs is run in order to mark all reachable nodes as non-obsolete. Also, the RESOLVE edges that target methods are allowed to be traversed in this last round in order to mark the target methods as needed for resolving (that is, only a stub of these methods is needed). - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class YShrink { - - private static final String CENTER_CLASS = "";//"y/view/NodeLabel"; - private static final String CENTER_METHOD_NAME = "";//"setOffsetDirty"; - private static final String CENTER_METHOD_DESC = ""; - - //private boolean showGraph = false; - private final boolean createStubs; - - private String digests; - - /** - * Instantiates a new Y shrink. - */ - public YShrink() { - this.createStubs = true; - } - - /** - * Instantiates a new Y shrink. - * - * @param createStubs the create stubs - * @param digests the digests - */ - public YShrink( boolean createStubs, String digests ) { - this.createStubs = createStubs; - this.digests = digests; - } - - /** - * Basic steps - * - Init model: create nodes for each class, method and field. Additionally, create a single entrypoint-node and one NEW-node for each class. - * - Mark all entrypoints: using a composite EntryPointFilter, mark/log every entrypoint-node in the model. - * - Create all dependency edges using the com.yworks.yshrink.core.Analyzer. - * - Create additional entrypoint-edges between the entrypoint-node and each entrypoint that doesn't represent an ordinary method (non-abstract,non-static,non-constructor). - * - Mark all obsolete classes, methods and fields using the com.yworks.yshrink.core.Shrinker. - * - Write out all non-obsolete classes using the com.yworks.yshrink.core.Writer. - * - * @param pairs the pairs - * @param epf the epf - * @param resolver the resolver - * @throws IOException the io exception - */ - public void doShrinkPairs( List pairs, EntryPointFilter epf, ClassResolver resolver ) throws - IOException { - - final Analyzer analyzer = new Analyzer(); - - Model model = new Model(); - - if ( null != resolver ) model.setClassResolver( resolver ); - - // create nodes - if ( ! model.isSimpleModelSet() ) { - analyzer.initModel( model, pairs ); - } - - // mark entrypoints - List entryPoints = markEntryPoints( model, epf ); - - // create edges - if ( model.isSimpleModelSet() ) { - analyzer.createDependencyEdges( model ); - } else { - analyzer.createEdges( model ); - } - model.createEntryPointEdges( entryPoints ); - - final Shrinker shrinker = new Shrinker(); - shrinker.shrink( model ); - - final Writer writer = new Writer(createStubs, digests ); - - for ( ShrinkBag bag : pairs ) { - if ( ! bag.isEntryPointJar() ) { - writer.write( model, bag ); - } - } - - if ( !model.isAllResolved() ) { - Logger.warn( "Not all dependencies could be resolved. Please see the logfile for details." ); - } - } - - /** - * If a constructor is an entrypoint, the synthetic new-node of its class is also marked as an entrypoint. - * - * - * @param model - * - * @param epFilter - */ - private List markEntryPoints( final Model model, - final EntryPointFilter epFilter ) { - - StringBuilder buf = new StringBuilder(); - buf.append( "\n" ); - - final List entryPoints = new ArrayList(); - - for ( ClassDescriptor cd : model.getAllClassDescriptors() ) { - - epFilter.setRetainAttribute( cd ); - - if ( epFilter.isEntryPointClass( model, cd ) ) { - buf.append( "\t\n" ); - entryPoints.add( cd ); - cd.setEntryPoint( true ); - model.markNotObsolete( cd.getNode() ); - } - for ( MethodDescriptor md : cd.getMethods() ) { - if ( epFilter.isEntryPointMethod( model, cd, md ) ) { - buf.append( - "\t\n" ); - - entryPoints.add( md ); - md.setEntryPoint( true ); - - if ( md.getName().equals( Model.CONSTRUCTOR_NAME ) ) { - AbstractDescriptor newNodeDesc = model.getDescriptor( cd.getNewNode() ); - if ( ! newNodeDesc.isEntryPoint() ) { - newNodeDesc.setEntryPoint( true ); - entryPoints.add( newNodeDesc ); - } - } - } - } - for ( FieldDescriptor fd : cd.getFields() ) { - if ( epFilter.isEntryPointField( model, cd, fd ) ) { - buf.append( "\t\n" ); - entryPoints.add( fd ); - fd.setEntryPoint( true ); - } - } - } - - buf.append( "\n" ); - Logger.shrinkLog( buf.toString() ); - - return entryPoints; - } - - /** - * Main. - * - * @param args the args - */ - public static void main( final String[] args ) { - - new ConsoleLogger(); - - try { - - boolean showGraph = false; - - File in = null; - File out = null; - - if ( args.length > 0 ) { - in = new File( args[ 0 ] ); - } - - if ( null == in ) { - in = new File( ClassLoader.getSystemResource( "yshrink.jar" ).getFile() ); - } - - if ( args.length > 1 ) { - out = new File( args[ 1 ] ); - } - - if ( null == out ) { - out = new File( System.getProperty( "user.dir" ) + "/out.jar" ); - } - - if ( args.length > 2 ) { - showGraph = Boolean.parseBoolean( args[ 2 ] ); - } - - final URL[] externalLibs = new URL[]{ - //ClassLoader.getSystemResource( "asm-2.2.2.jar" ), - //ClassLoader.getSystemResource( "y.jar" ), - ClassLoader.getSystemResource( "external.jar" ) - }; - - final YShrink yshrink = new YShrink(); - - final EntryPointFilter epf = new AllMainMethodsFilter(); - - } catch ( Exception e ) { - Logger.err( "An Exception occured.", e ); - } - } -} diff --git a/src/main/java/com/yworks/yshrink/YShrinkInvoker.java b/src/main/java/com/yworks/yshrink/YShrinkInvoker.java deleted file mode 100644 index c993b899..00000000 --- a/src/main/java/com/yworks/yshrink/YShrinkInvoker.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.yworks.yshrink; - -import com.yworks.common.ant.EntryPointsSection; -import com.yworks.common.ShrinkBag; -import com.yworks.yguard.ant.MethodSection; -import com.yworks.yguard.ant.ClassSection; -import com.yworks.yguard.ant.FieldSection; -import org.apache.tools.ant.Task; -import org.apache.tools.ant.types.Path; - -import java.io.File; - -/** - * The interface Y shrink invoker. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public interface YShrinkInvoker { - - /** - * Execute. - */ - public void execute(); - - /** - * Add pair. - * - * @param pair the pair - */ - public void addPair( ShrinkBag pair ); - - /** - * Sets resource class path. - * - * @param path the path - */ - public void setResourceClassPath( Path path ); - - /** - * Add class section. - * - * @param cs the cs - */ - public void addClassSection( ClassSection cs ); - - /** - * Add method section. - * - * @param ms the ms - */ - void addMethodSection( MethodSection ms ); - - /** - * Add field section. - * - * @param fs the fs - */ - void addFieldSection( FieldSection fs ); - - /** - * Sets enty points. - * - * @param eps the eps - */ - void setEntyPoints( EntryPointsSection eps ); - - /** - * Sets log file. - * - * @param shrinkLog the shrink log - */ - void setLogFile( File shrinkLog ); - - /** - * Sets context. - * - * @param task the task - */ - void setContext(Task task); -} diff --git a/src/main/java/com/yworks/yshrink/YShrinkInvokerImpl.java b/src/main/java/com/yworks/yshrink/YShrinkInvokerImpl.java deleted file mode 100644 index b2cb51a8..00000000 --- a/src/main/java/com/yworks/yshrink/YShrinkInvokerImpl.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.yworks.yshrink; - -import com.yworks.yguard.ant.PatternMatchedClassesSection; -import com.yworks.common.ShrinkBag; -import com.yworks.common.ant.EntryPointsSection; -import com.yworks.common.ant.TypePatternSet; -import com.yworks.yguard.obf.YGuardRule; -import com.yworks.yshrink.ant.ClassSection; -import com.yworks.yshrink.ant.FieldSection; -import com.yworks.yshrink.ant.MethodSection; -import com.yworks.common.ant.PatternMatchedSection; -import com.yworks.yshrink.ant.ShrinkTask; -import org.apache.tools.ant.Task; -import org.apache.tools.ant.types.Path; -import org.apache.tools.ant.types.PatternSet; - -import java.io.File; - -/** - * The type Y shrink invoker. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class YShrinkInvokerImpl implements YShrinkInvoker { - - /** - * The Shrink task. - */ - final ShrinkTask shrinkTask; - - /** - * The Eps. - */ - EntryPointsSection eps; - - /** - * Instantiates a new Y shrink invoker. - */ - public YShrinkInvokerImpl() { - shrinkTask = new ShrinkTask(); - eps = new EntryPointsSection( shrinkTask ); - } - - public void setEntyPoints( EntryPointsSection eps ) { - this.eps = eps; - } - - public void setLogFile( File shrinkLog ) { - shrinkTask.setLogFile( shrinkLog ); - } - - public void setContext(Task task) { - shrinkTask.setProject(task.getProject()); - shrinkTask.setOwningTarget(task.getOwningTarget()); - shrinkTask.setTaskName(task.getTaskName()); - shrinkTask.setLocation(task.getLocation()); - shrinkTask.setDescription(task.getDescription()); - shrinkTask.init(); - } - - public void execute() { - shrinkTask.setEntryPointsExternally( eps ); - shrinkTask.execute(); - } - - public void addPair( ShrinkBag pair ) { - shrinkTask.addConfiguredInOutPair( pair ); - } - - public void setResourceClassPath( Path path ) { - shrinkTask.setResourceClassPath( path ); - } - - public void addClassSection( com.yworks.yguard.ant.ClassSection cs ) { - - ClassSection yShrinkCS = new ClassSection(); - - addPatternSets( cs, yShrinkCS, "name" ); - - yShrinkCS.setClasses( YShrinkInvokerImpl.convertAccess( cs.getClassMode() ).name() ); - yShrinkCS.setFields( YShrinkInvokerImpl.convertAccess( cs.getFieldMode() ).name() ); - yShrinkCS.setMethods( YShrinkInvokerImpl.convertAccess( cs.getMethodMode() ).name() ); - - if ( null != cs.getName() ) { - yShrinkCS.setName( cs.getName() ); - } - eps.addConfiguredClass( yShrinkCS ); - } - - public void addMethodSection( com.yworks.yguard.ant.MethodSection ms ) { - - MethodSection yShrinkMS = new MethodSection(); - - addPatternSets( ms, yShrinkMS, "class" ); - - yShrinkMS.setName( ms.getName() ); - yShrinkMS.setClass( ms.getClassName() ); - - eps.addConfiguredMethod( yShrinkMS ); - } - - public void addFieldSection( com.yworks.yguard.ant.FieldSection fs ) { - - FieldSection yShrinkFS = new FieldSection(); - - addPatternSets( fs, yShrinkFS, "class" ); - - yShrinkFS.setName( fs.getName() ); - yShrinkFS.setClass( fs.getClassName() ); - - eps.addConfiguredField( yShrinkFS ); - } - - private void addPatternSets( PatternMatchedClassesSection yGuardSection, - com.yworks.common.ant.PatternMatchedSection yShrinkSection, String type ) { - if ( null != yGuardSection.getPatternSets() ) { - for ( PatternSet ps : (Iterable) yGuardSection.getPatternSets() ) { - TypePatternSet tps = new TypePatternSet(); - tps.append( ps, shrinkTask.getProject() ); - tps.setType( type ); - yShrinkSection.addPatternSet( tps, tps.getType() ); - } - } - } - - private static PatternMatchedSection.Access convertAccess( int yGuardAccess ) { - - switch ( yGuardAccess ) { - case YGuardRule.LEVEL_PRIVATE: - return PatternMatchedSection.Access.PRIVATE; - case YGuardRule.LEVEL_FRIENDLY: - return PatternMatchedSection.Access.FRIENDLY; - case YGuardRule.LEVEL_PROTECTED: - return PatternMatchedSection.Access.PROTECTED; - case YGuardRule.LEVEL_PUBLIC: - return PatternMatchedSection.Access.PUBLIC; - default: - return PatternMatchedSection.Access.NONE; - } - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/AntLogger.java b/src/main/java/com/yworks/yshrink/ant/AntLogger.java deleted file mode 100644 index eb7a7e17..00000000 --- a/src/main/java/com/yworks/yshrink/ant/AntLogger.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.yworks.yshrink.ant; - -import com.yworks.logging.Logger; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.Task; - -/** - * The type Ant logger. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class AntLogger extends Logger { - - private final Task task; - private final Project project; - - /** - * Instantiates a new Ant logger. - * - * @param project the project - * @param task the task - */ - public AntLogger( final Project project, final Task task ) { - this.project = project; - this.task = task; - register(); - } - - public void doLog( final String s ) { - project.log( task, s, Project.MSG_INFO ); - } - - public void doErr( final String s ) { - project.log( task, "ERROR: "+s, Project.MSG_ERR ); - } - - public void doWarn( String s ) { - //project.log( task, "WARNING: "+s, Project.MSG_WARN ); - } - - public void doWarnToLog( String s ) { - } - - public void doShrinkLog( String s ) { -// project.log( s, Project.MSG_INFO ); - } - - public void doErr( String s, Throwable ex ) { - project.log( task, "ERROR: "+s + "\n" + ex.getMessage(), Project.MSG_ERR ); - } - - public void close() { - unregister(); - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/ClassSection.java b/src/main/java/com/yworks/yshrink/ant/ClassSection.java deleted file mode 100644 index 90881652..00000000 --- a/src/main/java/com/yworks/yshrink/ant/ClassSection.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.yworks.yshrink.ant; - -import com.yworks.common.ant.TypePatternSet; -import com.yworks.common.ant.PatternMatchedSection; -import com.yworks.yshrink.util.Util; - -import java.util.EnumSet; - -/** - * The type Class section. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public final class ClassSection extends PatternMatchedSection { - - private String name; - private String extendsType; - private String implementsType; - private Access classAccess = Access.NONE; - private Access methodAccess = Access.NONE; - private Access fieldAccess = Access.NONE; - - { - types = EnumSet.of( - TypePatternSet.Type.NAME // , EXTENDS - - ); - } - - /** - * Gets name. - * - * @return the name - */ - public String getName() { - return name; - } - - /** - * Sets name. - * - * @param name the name - */ - public void setName( String name ) { - this.name = Util.toInternalClass( name ); - } - - /** - * Gets extends. - * - * @return the extends - */ - public String getExtends() { - return extendsType; - } - - /** - * Sets extends. - * - * @param extendsType the extends type - */ - public void setExtends( String extendsType ) { - this.extendsType = Util.toInternalClass( extendsType ); - } - - /** - * Gets implements. - * - * @return the implements - */ - public String getImplements() { - return implementsType; - } - - /** - * Sets implements. - * - * @param implementsType the implements type - */ - public void setImplements( String implementsType ) { - this.implementsType = Util.toInternalClass( implementsType ); - } - - @Override - public void setAccess( String access ) { - super.setAccess( access ); - setClassAccess( access ); - setMethodAccess( access ); - setFieldAccess( access ); - } - - /** - * Gets class access. - * - * @return the class access - */ - public Access getClassAccess() { - return classAccess; - } - - /** - * Sets class access. - * - * @param classAccessStr the class access str - */ - public void setClassAccess( String classAccessStr ) { - Access acc = accessValue( classAccessStr ); - - if ( null != acc ) { - this.classAccess = acc; - } - } - - /** - * Gets method access. - * - * @return the method access - */ - public Access getMethodAccess() { - return methodAccess; - } - - /** - * Sets method access. - * - * @param methodAccessStr the method access str - */ - public void setMethodAccess( String methodAccessStr ) { - Access acc = accessValue( methodAccessStr ); - - if ( null != acc ) { - this.methodAccess = acc; - } - } - - /** - * Sets classes. - * - * @param classAccess the class access - */ - public void setClasses( String classAccess ) { - setClassAccess( classAccess ); - } - - /** - * Sets methods. - * - * @param methodAccess the method access - */ - public void setMethods( String methodAccess ) { - setMethodAccess( methodAccess ); - } - - /** - * Gets field access. - * - * @return the field access - */ - public Access getFieldAccess() { - return fieldAccess; - } - - /** - * Sets field access. - * - * @param fieldAccessStr the field access str - */ - public void setFieldAccess( String fieldAccessStr ) { - - Access acc = accessValue( fieldAccessStr ); - - if ( null != acc ) { - this.fieldAccess = acc; - } - } - - /** - * Sets fields. - * - * @param fieldAccess the field access - */ - public void setFields( String fieldAccess ) { - setFieldAccess( fieldAccess ); - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/FieldSection.java b/src/main/java/com/yworks/yshrink/ant/FieldSection.java deleted file mode 100644 index e6a672e3..00000000 --- a/src/main/java/com/yworks/yshrink/ant/FieldSection.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.yworks.yshrink.ant; - -import com.yworks.common.ant.TypePatternSet; -import com.yworks.common.ant.PatternMatchedSection; -import com.yworks.yshrink.util.Util; - -import java.util.EnumSet; - -/** - * The type Field section. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class FieldSection extends PatternMatchedSection { - - private String name; - private String className; - private String type; - - { - types = EnumSet.of( - TypePatternSet.Type.NAME, - TypePatternSet.Type.CLASS - ); - } - - /** - * Gets name. - * - * @return the name - */ - public String getName() { - return name; - } - - /** - * Sets name. - * - * @param name the name - */ - public void setName( String name ) { - this.name = name; - } - - /** - * Gets class name. - * - * @return the class name - */ - public String getClassName() { - return className; - } - - /** - * Sets class. - * - * @param className the class name - */ - public void setClass( String className ) { - this.className = Util.toInternalClass( className ); - } - - /** - * Gets type. - * - * @return the type - */ - public String getType() { - return type; - } - - /** - * Sets type. - * - * @param type the type - */ - public void setType( String type ) { - this.type = Util.toInternalClass( type ); - } - - @Override - public TypePatternSet createPatternSet() { - TypePatternSet typePatternSet = newTypePatternSet(); - typePatternSet.setType( "class" ); - addPatternSet( typePatternSet, typePatternSet.getType() ); - return typePatternSet; - } - - /** - * Instantiates a type pattern set, - * subclasses may provide custom implementations. - * - * @return the new type pattern set - */ - protected TypePatternSet newTypePatternSet() { - return new TypePatternSet(); - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/MethodSection.java b/src/main/java/com/yworks/yshrink/ant/MethodSection.java deleted file mode 100644 index 3724abdd..00000000 --- a/src/main/java/com/yworks/yshrink/ant/MethodSection.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.yworks.yshrink.ant; - -import com.yworks.common.ant.TypePatternSet; -import com.yworks.common.ant.PatternMatchedSection; -import com.yworks.yshrink.util.Util; -import org.objectweb.asm.Type; - -import java.util.EnumSet; - -/** - * Used by ant to handle the method element. - */ -public class MethodSection extends PatternMatchedSection { - - private String signature; - private String name; - private String className; - private String returnType; - private String args; - private String throwsClause; - - - { - types = EnumSet.of( - TypePatternSet.Type.NAME, - TypePatternSet.Type.CLASS - ); - } - - /** - * Gets signature. - * - * @return the signature - */ - public String getSignature() { - return signature; - } - - /** - * Sets signature. - * - * @param signature the signature - */ - public void setSignature( String signature ) { - this.signature = signature; - String[] methodArr = Util.toNativeMethod( signature ); - String methodName = methodArr[ 0 ]; - String methodDesc = methodArr[ 1 ]; - setName( methodName ); - setReturnType( Util.toJavaType( Type.getReturnType( methodDesc ).getDescriptor() ) ); - - setArgs( Util.getArgumentString( Type.getArgumentTypes( methodDesc ) ) ); - } - - /** - * Gets args. - * - * @return the args - */ - public String getArgs() { - return args; - } - - /** - * Sets args. - * - * @param args the args - */ - public void setArgs( String args ) { - this.args = args; - } - - /** - * Sets name. - * - * @param name the name - */ - public void setName( String name ) { - - // in yGuard, the name-attribute is the signature. - if ( name.trim().indexOf( ' ' ) != -1 ) { - setSignature( name ); - } else { - this.name = name; - } - } - - /** - * Sets class. - * - * @param name the name - */ - public void setClass( String name ) { - this.className = Util.toInternalClass( name ); - } - - /** - * Gets name. - * - * @return the name - */ - public String getName() { - return name; - } - - /** - * Gets class name. - * - * @return the class name - */ - public String getClassName() { - return className; - } - - /** - * Gets return type. - * - * @return the return type - */ - public String getReturnType() { - return returnType; - } - - /** - * Sets return type. - * - * @param returnType the return type - */ - public void setReturnType( String returnType ) { - this.returnType = Util.toInternalClass( returnType ); - } - - /** - * Gets throws. - * - * @return the throws - */ - public String getThrows() { - return throwsClause; - } - - /** - * Sets throws. - * - * @param throwsClause the throws clause - */ - public void setThrows( String throwsClause ) { - this.throwsClause = throwsClause; - } - - @Override - public TypePatternSet createPatternSet() { - System.out.println( "MethodSection.createPatternSet" ); - TypePatternSet typePatternSet = newTypePatternSet(); - typePatternSet.setType( "class" ); - addPatternSet( typePatternSet, typePatternSet.getType() ); - return typePatternSet; - } - - /** - * Instantiates a type pattern set, - * subclasses may provide custom implementations. - * - * @return the new type pattern set - */ - protected TypePatternSet newTypePatternSet() { - return new TypePatternSet(); - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/ShrinkTask.java b/src/main/java/com/yworks/yshrink/ant/ShrinkTask.java deleted file mode 100644 index c6e8ada8..00000000 --- a/src/main/java/com/yworks/yshrink/ant/ShrinkTask.java +++ /dev/null @@ -1,384 +0,0 @@ -package com.yworks.yshrink.ant; - -import com.yworks.common.ShrinkBag; -import com.yworks.common.ant.*; -import com.yworks.common.ant.AttributesSection; -import com.yworks.util.Version; -import com.yworks.yguard.obf.classfile.ClassConstants; -import com.yworks.yshrink.YShrink; -import com.yworks.yshrink.ant.filters.*; -import com.yworks.logging.Logger; -import com.yworks.logging.XmlLogger; -import com.yworks.yshrink.util.MultiReleaseException; -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.types.PatternSet; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.zip.GZIPOutputStream; - -/** - * The type Shrink task. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class ShrinkTask extends YGuardBaseTask { - - private File logFile = new File( "yshrinklog.xml" ); - - private boolean createStubs = false; - - private String digests = "SHA-1,MD5"; - - protected EntryPointsSection entryPointsSection; - - /** - * Instantiates a new Shrink task. - */ - public ShrinkTask() { - super(); - } - - /** - * Instantiates a new Shrink task. - * - * @param mode the mode - */ - public ShrinkTask( boolean mode ) { - super( mode ); - } - - @Override - public void execute() throws BuildException { - getProject().log(this,"yGuard Shrinker v" + Version.getVersion() + " - http://www.yworks.com/products/yguard", Project.MSG_INFO); - - super.execute(); - - Logger xmlLogger = new XmlLogger( getLogWriter() ); - Logger antLogger = new AntLogger(getProject(), this); - - final EntryPointFilters epfs = new EntryPointFilters(); - - List attributesSections = entryPointsSection != null ? entryPointsSection.getAttributesSections() : this.attributesSections; - - if ( entryPointsSection != null ) { - - epfs.setExclude( entryPointsSection ); - - List methodSections = entryPointsSection.getMethodSections(); - List fieldSections = entryPointsSection.getFieldSections(); - List classSections = entryPointsSection.getClassSections(); - - if ( methodSections.size() > 0 ) { - MethodFilter mf = new MethodFilter( getProject() ); - for ( MethodSection ms : methodSections ) { - mf.addMethodSection( ms ); - } - epfs.addEntryPointFilter( mf ); - } - - if ( fieldSections.size() > 0 ) { - FieldFilter ff = new FieldFilter( getProject() ); - for ( FieldSection fs : fieldSections ) { - ff.addFieldSection( fs ); - } - epfs.addEntryPointFilter( ff ); - } - - if ( classSections.size() > 0 ) { - ClassFilter cf = new ClassFilter( getProject() ); - for ( ClassSection cs : classSections ) { - cf.addClassSection( cs ); - } - epfs.addEntryPointFilter( cf ); - } - - AttributeFilter attributeFilter = new AttributeFilter(getProject()); - if (entryPointsSection.isRiAnn()){ - addAttributesSection(attributeFilter, ClassConstants.ATTR_RuntimeInvisibleAnnotations); - } - if (entryPointsSection.isRiPann()){ - addAttributesSection(attributeFilter, ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations); - } - if (entryPointsSection.isRvAnn()){ - addAttributesSection(attributeFilter, ClassConstants.ATTR_RuntimeVisibleAnnotations); - } - if (entryPointsSection.isRvPann()){ - addAttributesSection(attributeFilter, ClassConstants.ATTR_RuntimeVisibleParameterAnnotations); - } - if (entryPointsSection.isRvTypeAnn()){ - addAttributesSection(attributeFilter, ClassConstants.ATTR_RuntimeVisibleTypeAnnotations); - } - if (entryPointsSection.isRiTypeAnn()){ - addAttributesSection(attributeFilter, ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations); - } - if (entryPointsSection.isSource()) { - addAttributesSection(attributeFilter, ClassConstants.ATTR_SourceFile); - } - if (entryPointsSection.isLtable()) { - addAttributesSection(attributeFilter, ClassConstants.ATTR_LineNumberTable); - } - if (entryPointsSection.isLttable()) { - addAttributesSection(attributeFilter, ClassConstants.ATTR_LocalVariableTypeTable); - } - if (entryPointsSection.isVtable()) { - addAttributesSection(attributeFilter, ClassConstants.ATTR_LocalVariableTable); - } - if (entryPointsSection.isDebugExtension()) { - addAttributesSection(attributeFilter, ClassConstants.ATTR_SourceDebug); - } - epfs.addEntryPointFilter(attributeFilter); - - // Never remove package-info annotation class - ClassFilter classFilter = new ClassFilter(getProject()); - ClassSection classSection = new ClassSection(); - PatternSet patternSet = new PatternSet(); - patternSet.setIncludes("**/package-info"); - classSection.addPatternSet(patternSet, TypePatternSet.Type.NAME); - classFilter.addClassSection(classSection); - - epfs.addEntryPointFilter(classFilter); - } - - if ( null != attributesSections && attributesSections.size() > 0 ) { - AttributeFilter af = new AttributeFilter( getProject() ); - for ( com.yworks.common.ant.AttributesSection as : attributesSections ) { - af.addAttributesSection( as ); - } - epfs.addEntryPointFilter( af ); - } - - if ( pairs == null ) { - throw new BuildException( "no files to shrink" ); - } else { - boolean containsInOutPair = false; - boolean containsEntryPointJar = false; - for ( ShrinkBag shrinkBag : pairs ) { - if ( shrinkBag.isEntryPointJar() ) { - - EntryPointJarFilter epjf = new EntryPointJarFilter( (EntryPointJar) shrinkBag ); - epfs.addEntryPointFilter( epjf ); - - containsEntryPointJar = true; - } else { - containsInOutPair = true; - } - } - - if ( ! containsInOutPair ) { - throw new BuildException( "no files to shrink" ); - } - - if ( ( ! containsEntryPointJar ) && ( null == entryPointsSection ) ) { - Logger.log( "no entrypoints given - using class access public and protected on all inoutpairs." ); - entryPointsSection = new EntryPointsSection( this ); - ClassFilter cf = new ClassFilter( getProject() ); - ClassSection cs = new ClassSection(); - cs.setAccess( "protected" ); - cf.addClassSection( cs ); - epfs.addEntryPointFilter( cf ); - epfs.setExclude( entryPointsSection ); - } - } - - ResourceCpResolver resolver = null; - - if ( resourceClassPath != null ) { - resolver = new ResourceCpResolver( resourceClassPath, this ); - } - - if (properties.containsKey("digests")) { - setDigests((String) properties.get("digests")); - } - - final YShrink yShrink = new YShrink( createStubs, digests ); - - //epfs.addEntryPointFilter( new SerializationFilter( getProject() ) ); - - try { - - yShrink.doShrinkPairs( pairs, epfs, resolver ); - } catch ( MultiReleaseException mre ) { - throw mre; - } catch ( RuntimeException rte ) { - if ( rte.getMessage() != null ) { - Logger.err( rte.getMessage(), rte ); - } - throw new BuildException( "yShrink encountered an unknown problem!", rte ); - } catch ( Throwable e ) { - if ( e.getMessage() != null ) { - Logger.err( e.getMessage(), e ); - } else { - Logger.err(e.getClass().getName(), e); - } - throw new BuildException( "yShrink encountered an unknown severe problem!", e ); - - } finally { - try { - resolver.close(); - } catch (Exception e) { - // can't do nothing about it - } - xmlLogger.close(); - antLogger.close(); - } - } - - private PrintWriter getLogWriter() { - PrintWriter log = null; - if ( logFile != null ) { - try { - if ( logFile.getName().endsWith( ".gz" ) ) { - log = new PrintWriter( - new BufferedWriter( - new OutputStreamWriter( - new GZIPOutputStream( - new FileOutputStream( logFile ) - ) - ) - ) - ); - } else { - log = new PrintWriter( new BufferedWriter( new FileWriter( logFile ) ) ); - } - } catch ( IOException ioe ) { - getProject().log( this, "Could not create logfile: " + ioe, Project.MSG_ERR ); - log = new PrintWriter( System.out ); - } - } else { - log = new PrintWriter( System.out ); - } - return log; - } - - /** - * Gets create stubs. - * - * @return the create stubs - */ - public boolean getCreateStubs() { - return createStubs; - } - - /** - * Sets create stubs. - * - * @param createStubs the create stubs - */ - public void setCreateStubs( boolean createStubs ) { - this.createStubs = createStubs; - } - - /** - * Gets digests. - * - * @return the digests - */ - public String getDigests() { - return digests; - } - - /** - * Sets digests. - * - * @param digests the digests - */ - public void setDigests( String digests ) { - this.digests = digests; - } - - /** - * Sets log file. - * - * @param file the file - */ - public void setLogFile( File file ) { - this.logFile = file; - } - - /** - * Used by ant to handle the nested entryPoint element. - * - * @return an EntryPointsSection instance - */ - public EntryPointsSection createEntryPoints() { - if ( this.entryPointsSection != null ) { - throw new IllegalArgumentException( "Only one entrypoints or expose element allowed!" ); - } - this.entryPointsSection = newEntryPointsSection( this ); - return entryPointsSection; - } - - /** - * Instantiates the nested entryPoint element, - * subclasses may provide custom implementations. - * - * @return a new EntryPointsSection instance - */ - protected EntryPointsSection newEntryPointsSection( YGuardBaseTask bt ) { - return new EntryPointsSection( bt ); - } - - /** - * not for ant, used if the ShrinkTask is created 'artificially'. - * - * @param eps the eps - */ - public void setEntryPointsExternally( EntryPointsSection eps ) { - this.entryPointsSection = eps; - } - - /** - * Create expose entry points section. - * - * @return the entry points section - */ - public EntryPointsSection createExpose() { - return createEntryPoints(); - } - - public Exclude createKeep() { - return createExpose(); - } - - public void addAttributesSections( List attributesSections ) { - if ( null != entryPointsSection ) { - for ( com.yworks.common.ant.AttributesSection attributesSection : attributesSections ) { - entryPointsSection.addConfiguredAttribute( attributesSection ); - } - } else { - if( null != this.attributesSections ) { - this.attributesSections.addAll(attributesSections); - } else { - this.attributesSections = attributesSections; - } - } - } - - /** - * Add configured entrypointjar. - * - * @param entrypointjar the entrypointjar - */ - public void addConfiguredEntrypointjar( final EntryPointJar entrypointjar ) { - if ( pairs == null ) pairs = new ArrayList(); - pairs.add( entrypointjar ); - } - - - private static void addAttributesSection( - final AttributeFilter attributeFilter, final String name - ) { - AttributesSection as = new AttributesSection(); - as.setName(name); - attributeFilter.addAttributesSection(as); - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/AbstractEntryPointFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/AbstractEntryPointFilter.java deleted file mode 100644 index d20f8ae6..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/AbstractEntryPointFilter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.FieldDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; - -/** - * The type Abstract entry point filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class AbstractEntryPointFilter implements EntryPointFilter { - - public boolean isEntryPointClass( final Model model, final ClassDescriptor cd ) { - return false; - } - - public boolean isEntryPointMethod( final Model model, final ClassDescriptor cd, final MethodDescriptor md ) { - return false; - } - - public boolean isEntryPointField( final Model model, final ClassDescriptor cd, final FieldDescriptor fd ) { - return false; - } - - public void setRetainAttribute( final ClassDescriptor cd ) { - // - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/AllMainMethodsFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/AllMainMethodsFilter.java deleted file mode 100644 index ebfeb1a1..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/AllMainMethodsFilter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; -import com.yworks.logging.Logger; - -/** - * marks all main methods as entry points - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class AllMainMethodsFilter extends AbstractEntryPointFilter { - - /** - * The Main desc. - */ - static String MAIN_DESC = "([Ljava/lang/String;)V"; - - @Override - public boolean isEntryPointMethod( final Model model, final ClassDescriptor cd, final MethodDescriptor md ) { - - if ( "main".equals( md.getName() ) ) { - Logger.log( "MainMethodFilter: main found in " + cd.getName() ); - return true; - } else { - return false; - } - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/AttributeFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/AttributeFilter.java deleted file mode 100644 index c5faeb3a..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/AttributeFilter.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.common.ant.AttributesSection; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.util.Util; -import com.yworks.common.ant.TypePatternSet; - -import java.util.List; -import java.util.ArrayList; - -import org.apache.tools.ant.Project; - -/** - * The type Attribute filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class AttributeFilter extends PatternMatchedFilter { - - private List sections = new ArrayList( ); - - /** - * Instantiates a new Attribute filter. - * - * @param p the p - */ - public AttributeFilter( Project p ) { - super( p ); - } - - /** - * Add attributes section. - * - * @param as the as - */ - public void addAttributesSection( AttributesSection as ) { - sections.add( as ); - } - - public void setRetainAttribute( ClassDescriptor cd ) { - - String className = cd.getName(); - String javaClassName = Util.toJavaClass( cd.getName() ); - - for ( AttributesSection section : sections ) { - if ( match( TypePatternSet.Type.NAME, javaClassName, section ) || - match( TypePatternSet.Type.NAME, className, section ) ) { - - for ( String attr : section.getAttributes() ) { - cd.setRetainAttribute( attr ); - } - } - } - } - - -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/ClassFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/ClassFilter.java deleted file mode 100644 index 93542c54..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/ClassFilter.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.common.ant.PatternMatchedSection; -import com.yworks.common.ant.TypePatternSet; -import com.yworks.yshrink.ant.ClassSection; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.FieldDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; -import com.yworks.yshrink.util.Util; -import org.apache.tools.ant.Project; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * The type Class filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class ClassFilter extends PatternMatchedFilter { - - private List sections; - - /** - * Instantiates a new Class filter. - * - * @param project the project - */ - public ClassFilter( Project project ) { - super( project ); - } - - @Override - public boolean isEntryPointClass( final Model model, final ClassDescriptor cd ) { - boolean r = false; - for ( ClassSection cs : sections ) { - if (matches(cs, model, cd)) { - return true; - } - } - return false; - } - - private boolean matches( final ClassSection cs, final Model model, final ClassDescriptor cd ) { - - String className = cd.getName(); - - boolean r = true; - - // name, access -// r &= isEntryPointClass( model, cd ); - - // access - if ( null != cs.getClassAccess() && ( cs.getName() == null || cs.getName() == "" ) && ( !cs.getClassAccess().equals( PatternMatchedSection.Access.NONE ) ) ) - { - r &= cs.getClassAccess().isAccessLevel( cd.getAccess() ); - } - - // name - String entryClassName = cs.getName(); - if ( null == entryClassName || entryClassName.length() == 0 ) { - r &= ( match( TypePatternSet.Type.NAME, Util.toJavaClass( className ), cs ) - || - match( TypePatternSet.Type.NAME, className, cs ) ); - } else { - r &= entryClassName.equals( className ); - } - - // extends - if ( null != cs.getExtends() ) { - boolean self = cs.getExtends().equals( cd.getName() ); - if ( !self ) { - Collection ancestors = cd.getAllAncestorClasses( model ); - r &= ancestors.contains( cs.getExtends() ); - } else { - r &= self; - } - } - - // implements - if ( null != cs.getImplements() ) { - boolean self = cs.getImplements().equals( cd.getName() ); - if ( !self ) { - Collection interfaces = cd.getAllImplementedInterfaces( model ); - r &= interfaces.contains( cs.getImplements() ); - } else { - r &= self; - } - } - - return r; - } - - private List getAllMatchingClassSections( final Model model, final ClassDescriptor cd ) { - - List matchingSections = new ArrayList(); - - for ( ClassSection cs : sections ) { - if ( matches( cs, model, cd ) ) { - matchingSections.add( cs ); - } - } - - return matchingSections; - } - - @Override - public boolean isEntryPointField( final Model model, final ClassDescriptor cd, final FieldDescriptor fd ) { - - for ( ClassSection cs : getAllMatchingClassSections( model, cd ) ) { - - boolean r = false; - - PatternMatchedSection.Access acc = cs.getFieldAccess(); - if ( null != acc ) { - r = acc.isAccessLevel( fd.getAccess() ); - } - - if ( r ) { - return true; - } - } - - return false; - } - - @Override - public boolean isEntryPointMethod( final Model model, final ClassDescriptor cd, final MethodDescriptor md ) { - - for ( ClassSection cs : getAllMatchingClassSections( model, cd ) ) { - - boolean r = true; - - PatternMatchedSection.Access acc = cs.getMethodAccess(); - if ( null != acc ) { - r = r && acc.isAccessLevel( md.getAccess() ); - } - - if ( r ) { - return true; - } - } - - return false; - } - - /** - * Add class section. - * - * @param cs the cs - */ - public void addClassSection( ClassSection cs ) { - if ( null == sections ) { - sections = new ArrayList( 5 ); - } - sections.add( cs ); - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/EntryPointFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/EntryPointFilter.java deleted file mode 100644 index 3d3acddc..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/EntryPointFilter.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.FieldDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; - -/** - * The interface Entry point filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public interface EntryPointFilter { - - /** - * Is entry point class boolean. - * - * @param model the model - * @param cd the cd - * @return the boolean - */ - public boolean isEntryPointClass( final Model model, final ClassDescriptor cd ); - - /** - * Is entry point method boolean. - * - * @param model the model - * @param cd the cd - * @param md the md - * @return the boolean - */ - public boolean isEntryPointMethod( final Model model, final ClassDescriptor cd, final MethodDescriptor md ); - - /** - * Is entry point field boolean. - * - * @param model the model - * @param cd the cd - * @param fd the fd - * @return the boolean - */ - public boolean isEntryPointField( final Model model, final ClassDescriptor cd, final FieldDescriptor fd ); - - /** - * Sets retain attribute. - * - * @param cd the cd - */ - public void setRetainAttribute( final ClassDescriptor cd ); - -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/EntryPointFilters.java b/src/main/java/com/yworks/yshrink/ant/filters/EntryPointFilters.java deleted file mode 100644 index 7b0a8579..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/EntryPointFilters.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.common.ant.Exclude; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.FieldDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; - -import java.util.ArrayList; -import java.util.List; - -/** - * The type Entry point filters. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class EntryPointFilters extends AbstractEntryPointFilter { - - /** - * The Filters. - */ - List filters; - - private Exclude exclude; - - /** - * Instantiates a new Entry point filters. - */ - public EntryPointFilters() { - this.filters = new ArrayList(); - } - - /** - * Sets exclude. - * - * @param exclude the exclude - */ - public void setExclude( Exclude exclude ) { - this.exclude = exclude; - } - - /** - * Add entry point filter. - * - * @param entryPointFilter the entry point filter - */ - public void addEntryPointFilter( final EntryPointFilter entryPointFilter ) { - filters.add( entryPointFilter ); - } - - public boolean isEntryPointClass( final Model model, final ClassDescriptor cd ) { - for ( EntryPointFilter entryPointFilter : filters ) { - if ( entryPointFilter.isEntryPointClass( model, cd ) ) { - return true; - } - } - return false; - } - - public boolean isEntryPointMethod( final Model model, final ClassDescriptor cd, final MethodDescriptor md ) { - for ( EntryPointFilter entryPointFilter : filters ) { - if ( entryPointFilter.isEntryPointMethod( model, cd, md ) ) { - return true; - } - } - return false; - } - - public boolean isEntryPointField( final Model model, final ClassDescriptor cd, final FieldDescriptor fd ) { - for ( EntryPointFilter entryPointFilter : filters ) { - if ( entryPointFilter.isEntryPointField( model, cd, fd ) ) { - return true; - } - } - return false; - } - - @Override - public void setRetainAttribute( final ClassDescriptor cd ) { - for ( EntryPointFilter entryPointFilter : filters ) { - entryPointFilter.setRetainAttribute( cd ); - } - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/EntryPointJarFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/EntryPointJarFilter.java deleted file mode 100644 index c5126e39..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/EntryPointJarFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.common.ant.EntryPointJar; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.FieldDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; - -/** - * The type Entry point jar filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class EntryPointJarFilter extends AbstractEntryPointFilter { - - private final EntryPointJar entryPointJar; - - /** - * Instantiates a new Entry point jar filter. - * - * @param entryPointJar the entry point jar - */ - public EntryPointJarFilter( EntryPointJar entryPointJar ) { - this.entryPointJar = entryPointJar; - } - - @Override - public boolean isEntryPointClass( final Model model, final ClassDescriptor cd ) { - return cd.getSourceJar().equals( entryPointJar.getIn() ); - } - - @Override - public boolean isEntryPointMethod( final Model model, final ClassDescriptor cd, final MethodDescriptor md ) { - return md.getSourceJar().equals( entryPointJar.getIn() ); - } - - @Override - public boolean isEntryPointField( final Model model, final ClassDescriptor cd, final FieldDescriptor fd ) { - return fd.getSourceJar().equals( entryPointJar.getIn() ); - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/FieldFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/FieldFilter.java deleted file mode 100644 index 63d4964e..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/FieldFilter.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.common.ant.TypePatternSet; -import com.yworks.yshrink.ant.FieldSection; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.FieldDescriptor; -import com.yworks.yshrink.model.Model; -import com.yworks.yshrink.util.Util; -import org.apache.tools.ant.Project; -import org.objectweb.asm.Type; - -import java.util.ArrayList; -import java.util.List; - -/** - * The type Field filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class FieldFilter extends PatternMatchedFilter { - - private List sections; - - /** - * Instantiates a new Field filter. - * - * @param project the project - */ - public FieldFilter( final Project project ) { - super( project ); - } - - /** - * Add field section. - * - * @param fieldSection the field section - */ - public void addFieldSection( FieldSection fieldSection ) { - if ( null == sections ) { - sections = new ArrayList( 5 ); - } - sections.add( fieldSection ); - } - - @Override - public boolean isEntryPointField( final Model model, final ClassDescriptor cd, final FieldDescriptor fd ) { - - String className = cd.getName(); - String fieldName = fd.getName(); - - for ( FieldSection fs : sections ) { - - boolean r = true; - - String entryFieldClass = fs.getClassName(); - String entryFieldName = fs.getName(); - - // type - if ( null != fs.getType() ) { - Type requiredType = Type.getType( Util.verboseToNativeType( fs.getType() ) ); - r &= ( requiredType.equals( fd.getDesc() ) ); - } - - // access - if ( null != fs.getAccess() ) { - r &= fs.getAccess().isAccessLevel( fd.getAccess() ); - } - - // class - if ( null == entryFieldClass || entryFieldClass.length() == 0 ) { - r &= match( TypePatternSet.Type.CLASS, className, fs ) || - match( TypePatternSet.Type.CLASS, Util.toJavaClass( className ), fs ); - } else { - r &= entryFieldClass.equals( className ); - } - - // name - if ( null == entryFieldName || entryFieldName.length() == 0 ) { - r &= match( TypePatternSet.Type.NAME, fieldName, fs ); - } else { - r &= entryFieldName.equals( fieldName ); - } - - if ( r ) { - return r; - } - } - - return false; - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/MethodFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/MethodFilter.java deleted file mode 100644 index f7f6e0e4..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/MethodFilter.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.common.ant.TypePatternSet; -import com.yworks.yshrink.ant.MethodSection; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; -import com.yworks.yshrink.util.Util; -import org.apache.tools.ant.Project; -import org.objectweb.asm.Type; - -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; - -/** - * The type Method filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class MethodFilter extends PatternMatchedFilter { - - private List sections; - - /** - * Instantiates a new Method filter. - * - * @param project the project - */ - public MethodFilter( Project project ) { - super( project ); - } - - /** - * Add method section. - * - * @param methodSection the method section - */ - public void addMethodSection( MethodSection methodSection ) { - if ( null == sections ) { - sections = new ArrayList( 5 ); - } - sections.add( methodSection ); - } - - @Override - public boolean isEntryPointMethod( final Model model, final ClassDescriptor cd, final MethodDescriptor md ) { - - String className = cd.getName(); - String methodName = md.getName(); - - for ( MethodSection ms : sections ) { - - String entryMethodName = ms.getName(); - String entryMethodClass = ms.getClassName(); - - boolean r = true; - - // returnType - if ( null != ms.getReturnType() ) { - Type requiredReturnType = Type.getType( Util.verboseToNativeType( ms.getReturnType() ) ); - r &= ( requiredReturnType.equals( md.getReturnType() ) ); - } - - // arguments - if ( null != ms.getArgs() ) { - String[] requiredArgTypes = ms.getArgs().split( "\\s*,\\s*" ); - if ( requiredArgTypes.length == 1 && requiredArgTypes[ 0 ].length() == 0 ) { // args="" - requiredArgTypes = new String[0]; - } - Type[] argTypes = md.getArgumentTypes(); - - if ( requiredArgTypes.length == argTypes.length ) { - for ( int i = 0; i < argTypes.length; i++ ) { - Type argType = argTypes[ i ]; - Type requiredArgType = Type.getType( Util.verboseToNativeType( requiredArgTypes[ i ].trim() ) ); - - r &= argType.equals( requiredArgType ); - } - } else { - r = false; - } - } - - // access - if ( null != ms.getAccess() ) { - r &= ms.getAccess().isAccessLevel( md.getAccess() ); - } - - // class - if ( null == entryMethodClass || entryMethodClass.length() == 0 ) { - r &= match( TypePatternSet.Type.CLASS, className, ms ) || - match( TypePatternSet.Type.CLASS, Util.toJavaClass( className ), ms ); - } else { - r &= entryMethodClass.equals( className ); - } - - // throws - if ( null != ms.getThrows() ) { - - StringTokenizer tokenizer = new StringTokenizer( ms.getThrows(), "," ); - - while ( tokenizer.hasMoreTokens() ) { - String exception = Util.toInternalClass( tokenizer.nextToken().trim() ); - - - - boolean found = false; - - if ( null != md.getExceptions() ) { - for ( String exception2 : md.getExceptions() ) { - if ( exception2.equals( exception ) ) { - found = true; - } - } - } - - r &= found; - - } - } - - // name - if ( null == entryMethodName || entryMethodName.length() == 0 ) { - r &= match( TypePatternSet.Type.NAME, methodName, ms ); - } else { - r &= entryMethodName.equals( methodName ); - } - - if ( r ) { - return r; - } - } - - return false; - } -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/PatternMatchedFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/PatternMatchedFilter.java deleted file mode 100644 index d5b835dd..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/PatternMatchedFilter.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import com.yworks.common.ant.TypePatternSet; -import com.yworks.common.ant.PatternMatchedSection; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.types.PatternSet; -import org.apache.tools.ant.types.selectors.SelectorUtils; - -/** - * The type Pattern matched filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class PatternMatchedFilter extends AbstractEntryPointFilter { - - private Project project; - - /** - * Instantiates a new Pattern matched filter. - * - * @param p the p - */ - public PatternMatchedFilter( final Project p ) { - project = p; - } - - /** - * Match boolean. - * - * @param type the type - * @param str the str - * @param section the section - * @return the boolean - */ - protected boolean match( TypePatternSet.Type type, String str, PatternMatchedSection section ) { - - PatternSet patternSet = section.getPatternSet( type ); - - if ( patternSet != null ) { - - String[] excludePatterns = patternSet.getExcludePatterns( project ); - if ( null != excludePatterns ) { - for ( String excludePattern : excludePatterns ) { - if ( SelectorUtils.match( excludePattern, str ) ) { - return false; - } - } - } - - String[] includePatterns = patternSet.getIncludePatterns( project ); - if ( null != includePatterns ) { - for ( String includePattern : includePatterns ) { - if ( SelectorUtils.match( includePattern, str ) ) { - return true; - } - } - } else { - return true; // no include given: include all - } - } else { - return true; // no patternset for type given: include all - } - return false; // str wasnt contained in includes / excludes - } - - -} diff --git a/src/main/java/com/yworks/yshrink/ant/filters/SerializationFilter.java b/src/main/java/com/yworks/yshrink/ant/filters/SerializationFilter.java deleted file mode 100644 index e9d9ae25..00000000 --- a/src/main/java/com/yworks/yshrink/ant/filters/SerializationFilter.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.yworks.yshrink.ant.filters; - -import org.apache.tools.ant.Project; -import com.yworks.yshrink.ant.MethodSection; -import com.yworks.yshrink.model.Model; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.MethodDescriptor; - -import java.util.Collection; - -/** - * The type Serialization filter. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class SerializationFilter extends MethodFilter { - - /** - * Instantiates a new Serialization filter. - * - * @param project the project - */ - public SerializationFilter( Project project ) { - super( project ); - - MethodSection msWrite = new MethodSection(); - msWrite.setSignature( "void writeObject(java.io.ObjectOutputStream)" ); - msWrite.setAccess( "private" ); - addMethodSection( msWrite ); - - MethodSection msRead = new MethodSection(); - msRead.setSignature( "void readObject(java.io.ObjectInputStream)" ); - msRead.setAccess( "private" ); - addMethodSection( msRead ); - - } - - @Override - public boolean isEntryPointMethod( final Model model, final ClassDescriptor cd, final MethodDescriptor md ) { - - boolean r = false; - - Collection interfaces = cd.getAllImplementedInterfaces( model ); - if ( interfaces.contains( "java/io/Serializable" ) ) { - r = true; - } - - return r && super.isEntryPointMethod( model, cd, md ); - } -} diff --git a/src/main/java/com/yworks/yshrink/core/Analyzer.java b/src/main/java/com/yworks/yshrink/core/Analyzer.java index 66dcdb49..163ee600 100644 --- a/src/main/java/com/yworks/yshrink/core/Analyzer.java +++ b/src/main/java/com/yworks/yshrink/core/Analyzer.java @@ -41,16 +41,6 @@ public class Analyzer { private static final String SYNTHETIC_DOT_CLASS_FIELD_START = "class$"; private static final String CLASS_DESC = "Ljava/lang/Class;"; - /** - * Create edges. - * - * @param model the model - */ - public void createEdges( Model model ) { - createInheritanceEdges( model ); - createDependencyEdges( model ); - } - /** * Create all nodes needed for dependency analysis using a ModelVisitor. Also creates artificial * <clinit> nodes for each (non-inner) class if not already present. @@ -159,100 +149,6 @@ public void createInheritanceEdges( final Model model ) { model.setSimpleModelSet(); } - /** - * Create all kinds of dependency edges for the whole model. - * - * @param model the model - */ - public void createDependencyEdges( final Model model ) { - - for ( ClassDescriptor cd : model.getAllClassDescriptors() ) { - - createAnnotationEdges(cd, model); - - model.createDependencyEdge( cd.getNewNode(), cd.getNode(), EdgeType.MEMBER_OF ); - - createInnerClassEdges( model, cd ); - createAssumeEdges( model, cd ); - - for ( MethodDescriptor md : cd.getMethods() ) { - - createAnnotationEdges(md, model); - - model.createDependencyEdge( md, cd, EdgeType.MEMBER_OF ); - - createReferenceEdges( model, md ); - createMethodSignatureEdges( model, md ); - createInvokeEdges( model, cd, md ); - createTypeInstructionEdges( model, md ); - } - for ( FieldDescriptor fd : cd.getFields() ) { - createAnnotationEdges(fd, model); - model.createDependencyEdge( fd, cd, EdgeType.MEMBER_OF ); - - // resolve edge for field type - // not required for verification, but obfuscator will complain if the type is not found. - String fieldTypeName = Util.getTypeNameFromDescriptor(fd.getDesc()); - if (model.isClassModeled(fieldTypeName)) { - ClassDescriptor fieldType = model.getClassDescriptor(fieldTypeName); - model.createDependencyEdge(fd, fieldType, EdgeType.RESOLVE); - } - - } - } - } - - private void createAnnotationEdges(AbstractDescriptor cd, Model model) { - for (AnnotationUsage annotationUsage : cd.getAnnotations()) { - if (model.isClassModeled(annotationUsage.getDescriptor())) { - ClassDescriptor annotationClassDescriptor = model.getClassDescriptor(annotationUsage.getDescriptor()); - model.createDependencyEdge(cd, annotationClassDescriptor, EdgeType.REFERENCES); - - for (String field : annotationUsage.getFieldUsages()) { - for (MethodDescriptor methodDescriptor : annotationClassDescriptor.getMethods()) { - if (methodDescriptor.getName().equals(field)) { - model.createDependencyEdge(cd, methodDescriptor, EdgeType.RESOLVE); - break; - } - } - } - } - } - } - - /** - * Create RESOLVE edges from md to the runtime type of type instructions ANEWARRAY, MULTIANEWARRAY, - * INSTANCEOF, CHECKCAST and LDC (if class version >= 49.0). Create CREATES edges for NEW instructions from - * md to the NEW-node of the given runtime type. - */ - private void createTypeInstructionEdges( final Model model, final MethodDescriptor md ) { - for ( AbstractMap.SimpleEntry typeInstruction : md.getTypeInstructions() ) { - final int opcode = (Integer) typeInstruction.getKey(); - final String desc = (String) typeInstruction.getValue(); - - final String type = Util.getTypeNameFromDescriptor( desc ); - - if ( opcode == Opcodes.ANEWARRAY - || opcode == Opcodes.MULTIANEWARRAY - || opcode == Opcodes.INSTANCEOF - || opcode == Opcodes.CHECKCAST - || opcode == Opcodes.LDC ) // .class, version >= 49.0 - { - - if ( model.isClassModeled( type ) ) { - ClassDescriptor cd = model.getClassDescriptor( type ); - model.createDependencyEdge( md, cd, EdgeType.RESOLVE ); - } - } else if ( opcode == Opcodes.NEW ) { - - if ( model.isClassModeled( type ) ) { - ClassDescriptor targetClass = model.getClassDescriptor( type ); - model.createDependencyEdge( md.getNode(), targetClass.getNewNode(), EdgeType.CREATES ); - } - } - } - } - private void createEnumEdges( final Model model, final ClassDescriptor cd ) { @@ -291,593 +187,4 @@ private void createEnumEdges( final Model model, final ClassDescriptor cd ) { } } } - - /** - * create ASSUME edges: dependencies from the NEW-node of cd to methods of class cd - * implementing/overriding methods of external interfaces/classes. If any ancestor class/interface of cd - * cannot be resolved, all non-private methods are assumed to be called. Also create ASSUME edges from the NEW-node of - * cd to all non-static, non-constructor methods that are marked as entrypoints and that are implemented - * in cd or an ancestor class of cd. - * - * @param model - * @param cd - */ - private void createAssumeEdges( final Model model, final ClassDescriptor cd ) { - - if ( cd.isInterface() ) { - return; - } - - Object newNode = cd.getNewNode(); - - if ( newNode == null ) { - Logger.err( "no NEW-Node found for " + cd.getName() ); - return; - } - - List externalMethods = new ArrayList( 5 ); - - boolean resolvable = model.getAllExternalAncestorMethods( cd.getName(), externalMethods ); - - if ( resolvable ) { - for ( Method method : externalMethods ) { - String mName = method.getName(); - String mDesc = Type.getMethodDescriptor( method ); - - if ( cd.implementsMethod( mName, mDesc ) ) { - - model.createDependencyEdge( newNode, cd.getMethod( mName, mDesc ).getNode(), EdgeType.ASSUME ); - } else { - List modeledClasses = new ArrayList<>(); - for ( String interfaceName: cd.getInterfaces() ) { - if ( model.isClassModeled( interfaceName ) ) modeledClasses.add( model.getClassDescriptor( interfaceName ) ); - } - if ( model.isClassModeled( cd.getSuperName() ) ) modeledClasses.add( model.getClassDescriptor( cd.getSuperName() ) ); - - for ( ClassDescriptor superDescriptor: modeledClasses ) { - createEdgeToImplementingMethod( superDescriptor, mName, mDesc, model, newNode, EdgeType.ASSUME, false ); - } - } - } - } else { // assume all non-private methods are called. - for ( MethodDescriptor md : cd.getMethods() ) { - if ( ! md.isPrivate() ) { - model.createDependencyEdge( newNode, md.getNode(), EdgeType.ASSUME ); - } - } - } - - List internalMethods = new ArrayList(); - model.getAllInternalAncestorEntrypointMethods( cd.getName(), internalMethods ); - - for ( MethodDescriptor md : internalMethods ) { - - String mName = md.getName(); - String mDesc = md.getDesc(); - - if ( !md.isStatic() || mName.equals( Model.CONSTRUCTOR_NAME ) ) { - - if ( cd.implementsMethod( mName, mDesc ) ) { - model.createDependencyEdge( newNode, cd.getMethod( mName, mDesc ).getNode(), EdgeType.ASSUME ); - } else { - if ( model.isClassModeled( cd.getSuperName() ) ) { - ClassDescriptor superCd = model.getClassDescriptor( cd.getSuperName() ); - - createEdgeToImplementingMethod( superCd, mName, mDesc, model, newNode, EdgeType.ASSUME, false ); - } - } - } - } - } - - /** - * create INVOKES edges: For all method invocations mi in method md: - *
    - *
  • mi is constructor: - *
      - *
    • mi is first invocation in md, md is constructor as well: - * create CHAIN dependency from md to mi.
    • - *
    • else: add CREATES dependency from - * md to mi. - *
    - *
  • else: see documentation of createEdgeToImplementingMethod, - * createSubtreeEdges
- * - * @param model - * @param cd - * @param md - */ - private void createInvokeEdges( final Model model, final ClassDescriptor cd, final MethodDescriptor md ) { - - for ( Invocation invocation : md.getInvocations() ) { - - final int opcode = invocation.getOpcode(); - final String targetType = invocation.getType(); - final String targetMethod = invocation.getName(); - final String targetDesc = invocation.getDesc(); - - if ( model.isClassModeled( targetType ) ) { // else: external class (ignored) - - ClassDescriptor target = model.getClassDescriptor( targetType ); - - // super calls: CHAIN and SUPER edges. - if ( opcode == Opcodes.INVOKESPECIAL && - targetType.equals( cd.getSuperName() ) ) { - // "CHAIN" calls to super constructor - if ( Model.CONSTRUCTOR_NAME.equals( targetMethod ) && - Model.CONSTRUCTOR_NAME.equals( md.getName() ) ) { - - final MethodDescriptor initMethod = target.getMethod( targetMethod, targetDesc ); - - model.createDependencyEdge( md, - initMethod, - EdgeType.CHAIN ); - } else { // calls to super-methods - - while ( ! target.implementsMethod( targetMethod, targetDesc ) && - model.isClassModeled( target.getSuperName() ) ) { - target = model.getClassDescriptor( target.getSuperName() ); - } - - if ( target.implementsMethod( targetMethod, targetDesc ) ) { - model.createDependencyEdge( md, - target.getMethod( targetMethod, targetDesc ), - EdgeType.SUPER ); - } - } - } else { - - if ( target.isInterface() || target.isAbstract() ) { - -// ClassDescriptor temp = findDeclaringClass( model, target, targetMethod, targetDesc ); -// if ( temp != null ) { -// target = temp; -// } - createEdgeToDeclaration( model, target, targetMethod, targetDesc, md ); - } - - // RULE 1.1.1 - createEdgesToAncestorMethods( model, target, md, targetMethod, targetDesc ); - - if ( ! targetMethod.equals( Model.CONSTRUCTOR_NAME ) ) { - // RULE 1.1.2 - createSubtreeEdges( model, cd, target, md, targetMethod, targetDesc ); - } - } - - if ( opcode == Opcodes.INVOKEDYNAMIC ) { - final MethodDescriptor initMethod = target.getMethod( targetMethod, targetDesc ); - model.createDependencyEdge(md, initMethod, EdgeType.INVOKEDYNAMIC); - } - } - } - } - - /** - * Create a RESOLVE dependency from source to the first declaration of targetMethod found in - * target or any ascendant abstract class or interface of target. (Although this method may - * call itself recursively with a concrete class as targetClass, it should be called initially only with - * interfaces or abstract classes as targetClass, since its purpose is to find a method declaration in - * case the runtime type a method is called on is an abstract class or interface. - * - * @param model - * @param targetClass - * @param targetMethod - * @param targetDesc - * @param source - */ - private void createEdgeToDeclaration( final Model model, ClassDescriptor targetClass, - final String targetMethod, - final String targetDesc, MethodDescriptor source ) { - - if ( targetClass.implementsMethod( targetMethod, targetDesc ) - && ( targetClass.isAbstract() || targetClass.isInterface() ) ) { - model.createDependencyEdge( source, targetClass.getMethod( targetMethod, targetDesc ), EdgeType.RESOLVE ); - return; - } - - String[] interfaces = targetClass.getInterfaces(); - if ( null != interfaces ) { - - for ( String interfc : interfaces ) { - if ( model.isClassModeled( interfc ) ) { - ClassDescriptor interfaceDesc = model.getClassDescriptor( interfc ); - createEdgeToDeclaration( model, interfaceDesc, targetMethod, targetDesc, source ); - } - } - } - - if ( !targetClass.isInterface() ) { - String superName = targetClass.getSuperName(); - if ( model.isClassModeled( superName ) ) { - ClassDescriptor superDesc = model.getClassDescriptor( superName ); - createEdgeToDeclaration( model, superDesc, targetMethod, targetDesc, source ); - } - } - } - - /** - *
  • owner is an interface: run this method on all concrete subclasses of class - * owner, add INVOKE dependencies to all subclasses of all owner-implementations that - * override targetMethod.
  • owner is a concrete class: add INVOKES dependency to the - * implementation of in class owner or any superclass of owner. While - * searching for the implementation, add RESOLVE dependency to all visited classes.
- *

- *

- * TODO think (interfaces) - * - * @param model - * @param owner - * @param md - * @param targetMethod - * @param targetDesc - */ - private void createEdgesToAncestorMethods( final Model model, ClassDescriptor owner, final MethodDescriptor md, - final String targetMethod, final String targetDesc ) { - - if ( owner.isInterface() ) { - - final Set implementingClasses = model.getAllImplementingClasses( owner ); - - if ( implementingClasses != null ) { - for ( ClassDescriptor ownerImpl : implementingClasses ) { - createEdgesToAncestorMethods( model, ownerImpl, md, targetMethod, targetDesc ); - createSubtreeEdges( model, owner, ownerImpl, md, targetMethod, targetDesc ); - } - } - } - - createEdgeToImplementingMethod( owner, targetMethod, targetDesc, model, md, EdgeType.INVOKES, true ); - } - - private void createEdgeToImplementingMethod( ClassDescriptor owner, String targetMethod, String targetDesc, - Model model, MethodDescriptor md, - EdgeType type, boolean createResolveEdge ) { - - createEdgeToImplementingMethod( owner, targetMethod, targetDesc, model, md.getNode(), type, createResolveEdge ); - } - - /** - * Finds all preceding interfaces relative to start. - * - * @param start the interface to start with - * @param path a initial path, should include the start element - * @param paths a empty list of paths that all paths will be appended to - */ - private void findSuperInterfaces( Model model, ClassDescriptor start, List path, List> paths ) { - path.add(start); - - boolean hasModeled = false; - - final int oldSize = path.size(); - for (String interfaceName : start.getInterfaces()) { - if (model.isClassModeled(interfaceName)) { - hasModeled = true; - if (path.size() > oldSize) { - path = new ArrayList(path.subList(0, oldSize)); - } - findSuperInterfaces(model, model.getClassDescriptor(interfaceName), path, paths); - } - } - - if (!hasModeled) { - paths.add(path); - } - } - - /** - * create a dependency edge to a concrete implementation of targetMethod in owner or any - * concrete superclass of owner. - * - * @param owner the class which targetMethod was called on. - * @param targetMethod - * @param targetDesc - * @param model - * @param node the source node of the dependency. - * @param type the EdgeType to use for the dependency edge. - * @param createResolveEdge wether to create an additional RESOLVE edge. - */ - private void createEdgeToImplementingMethod( ClassDescriptor owner, final String targetMethod, final String targetDesc, - Model model, Object node, - EdgeType type, boolean createResolveEdge ) { - - ArrayList classHierarchy = new ArrayList(); - classHierarchy.add(owner); - while ( ! owner.implementsMethod( targetMethod, targetDesc ) && - model.isClassModeled( owner.getSuperName() ) ) { - model.createDependencyEdge( node, owner.getNode(), EdgeType.RESOLVE ); - owner = model.getClassDescriptor( owner.getSuperName() ); - classHierarchy.add(owner); - } - if ( owner.implementsMethod( targetMethod, targetDesc ) ) { - - final MethodDescriptor targetMethodImp = owner.getMethod( targetMethod, targetDesc ); - - model.createDependencyEdge( node, targetMethodImp.getNode(), type ); - // RESOLVE dependency needed since INVOKES-dependency edge might not be traversed if owner is not instantiated. - if ( createResolveEdge && !owner.isInterface() ) { - model.createDependencyEdge( node, targetMethodImp.getNode(), EdgeType.RESOLVE ); - } - - // static methods: RESOLVE dependency to implementing class - if ( targetMethodImp.isStatic() ) { - model.createDependencyEdge( node, owner.getNode(), EdgeType.RESOLVE ); - } - // method is not implemented by any super class of owner, thus it must be a default method inherited from a interface - } else { - // gather all direct interfaces of the class and its super classes - final HashSet seen = new HashSet(); - final ArrayList interfaceDescriptors = new ArrayList(); - if (owner.isInterface()) { - seen.add(owner.getName()); - interfaceDescriptors.add(owner); - } - for (ClassDescriptor cd: classHierarchy) { - for (String interfaceName : cd.getInterfaces()) { - if (seen.add(interfaceName) && model.isClassModeled(interfaceName)) { - interfaceDescriptors.add(model.getClassDescriptor(interfaceName)); - } - } - } - - // find all paths from all direct interfaces to their super interfaces - final List> interfaceHierarchies = new ArrayList>(); - for (ClassDescriptor cd: interfaceDescriptors) { - findSuperInterfaces(model, cd, new ArrayList(), interfaceHierarchies); - } - - // determine the most specific interface implementation - int mostSpecificDist = 0; - ClassDescriptor mostSpecific = null; - for (List hierarchy : interfaceHierarchies) { - final int idx = indexOf(hierarchy, targetMethod, targetDesc); - if (idx > -1) { - final int dist = lastIndexOf(hierarchy, targetMethod, targetDesc) - idx + 1; - if (mostSpecificDist < dist) { - mostSpecificDist = dist; - mostSpecific = hierarchy.get(idx); - } - } - } - - if ( mostSpecific != null ) { - final MethodDescriptor targetMethodImp = mostSpecific.getMethod( targetMethod, targetDesc ); - - model.createDependencyEdge( node, targetMethodImp.getNode(), type ); - // RESOLVE dependency needed since INVOKES-dependency edge might not be traversed if owner is not instantiated. - if ( createResolveEdge ) { - model.createDependencyEdge( node, targetMethodImp.getNode(), EdgeType.RESOLVE ); - } - - // default methods: RESOLVE dependency to implementing interface - if ( targetMethodImp.hasFlag(Opcodes.ACC_PUBLIC) && !targetMethodImp.hasFlag(Opcodes.ACC_ABSTRACT) ) { - model.createDependencyEdge( node, owner.getNode(), EdgeType.RESOLVE ); - } - } - } - } - - /** - * Determines the index of the first class descriptor in the given list that - * {@link ClassDescriptor#implementsMethod(String, String) implements} the - * method identified by the given method name and method descriptor. - * - * @return the index of the first class descriptor that implements the given - * method or {@code -1} if there is no such descriptor in the given list. - */ - private static int indexOf( - final List interfaces, - final String methodName, final String methodDescriptor - ) { - int idx = -1; - for (ClassDescriptor cd : interfaces) { - ++idx; - if (cd.implementsMethod(methodName, methodDescriptor)) { - return idx; - } - } - return -1; - } - - /** - * Determines the index of the last class descriptor in the given list that - * {@link ClassDescriptor#implementsMethod(String, String) implements} the - * method identified by the given method name and method descriptor. - * @return the index of the last class descriptor that implements the given - * method or {@code -1} if there is no such descriptor in the given list. - */ - private static int lastIndexOf( - final List interfaces, - final String methodName, final String methodDescriptor - ) { - int idx = interfaces.size(); - for (ListIterator it = interfaces.listIterator(idx); it.hasPrevious();) { - --idx; - final ClassDescriptor cd = it.previous(); - if (cd.implementsMethod(methodName, methodDescriptor)) { - return idx; - } - } - return -1; - } - - /** - * create INVOKES edges to all subclasses of class cd that override method targetMethod. - * - * @param model - * @param cd - * @param target - * @param mm - * @param targetMethod - * @param targetDesc - */ - private void createSubtreeEdges( final Model model, final ClassDescriptor cd, final ClassDescriptor target, - final MethodDescriptor mm, - final String targetMethod, final String targetDesc ) { - - final List subClasses = new ArrayList(); - model.getInternalDescendants( target, subClasses ); - - for ( ClassDescriptor targetSubclass : subClasses ) { - if ( targetSubclass != cd ) { - if ( targetSubclass.implementsMethod( targetMethod, targetDesc ) ) { - model.createDependencyEdge( mm, targetSubclass.getMethod( targetMethod, targetDesc ), EdgeType.INVOKES ); - } - } - } - } - - /** - * add RESOLVE dependency from source to the return type and all declared parameters and exceptions of - * source. - * - * @param model - * @param source - */ - private void createMethodSignatureEdges( final Model model, final MethodDescriptor source ) { - - // arguments - for ( Type argumentType : source.getArgumentTypes() ) { - final String className = Util.getTypeNameFromDescriptor( argumentType.getDescriptor() ); - if ( model.isClassModeled( className ) ) { - model.createDependencyEdge( source, model.getClassDescriptor( className ), EdgeType.RESOLVE ); - } - } - - // return type - final Type returnType = source.getReturnType(); - final String className = Util.getTypeNameFromDescriptor( returnType.getDescriptor() ); - if ( model.isClassModeled( className ) ) { - model.createDependencyEdge( source, model.getClassDescriptor( className ), EdgeType.RESOLVE ); - } - - // Exceptions - if ( source.getExceptions() != null ) { - for ( String exception : source.getExceptions() ) { - if ( model.isClassModeled( exception ) ) { - final ClassDescriptor target = model.getClassDescriptor( exception ); - model.createDependencyEdge( source, target, EdgeType.RESOLVE ); - } - } - } - } - - /** - * if cd is an inner class, add ENCLOSE dependency to enclosing class or enclosing method. - * - * @param model - * @param cd - */ - private void createInnerClassEdges( final Model model, final ClassDescriptor cd ) { - - if ( cd.isInnerClass() ) { - final ClassDescriptor enclosingClass = model.getClassDescriptor( cd.getEnclosingClass() ); - model.createDependencyEdge( cd, enclosingClass, EdgeType.ENCLOSE ); - } - if ( cd.getEnclosingMethod() != null ) { - final ClassDescriptor enclosingClass = model.getClassDescriptor( cd.getEnclosingClass() ); - MethodDescriptor enclosingMethodDescriptor = enclosingClass.getMethod(cd.getEnclosingMethod()); - if (null == enclosingMethodDescriptor) { - Logger.log("Missing enclosing method declaration in "+enclosingClass.getName()+ ": "+cd.getEnclosingMethod().getValue()); - } else { - model.createDependencyEdge( cd, enclosingMethodDescriptor, EdgeType.ENCLOSE ); - } - } - } - - /** - * for all field references f in md, add REFERENCES dependency to the field declaration found in - * the owner class/interface of f or any ancestor interface/class of the owner class. while searching for the - * declaration of f, add RESOLVE dependency to all visited classes/interfaces. - * - * @param model - * @param md - */ - private void createReferenceEdges( final Model model, final MethodDescriptor md ) { - - for ( String[] fieldRef : md.getFieldRefs() ) { - - final String refDesc = fieldRef[ 0 ]; - final String refName = fieldRef[ 1 ]; - - if ( model.isClassModeled( refDesc ) ) { - - ClassDescriptor owner = model.getClassDescriptor( refDesc ); - boolean declarationFound = owner.declaresField( refName ); - - while ( model.isClassModeled( owner.getSuperName() ) && !declarationFound ) { - if ( ! owner.declaresField( refName ) ) { // declared in interfaces? - model.createDependencyEdge( md, owner, EdgeType.RESOLVE ); - - for ( String interfc : owner.getInterfaces() ) { - if ( model.isClassModeled( interfc ) ) { - final ClassDescriptor interfcDesc = model.getClassDescriptor( interfc ); - if ( interfcDesc.declaresField( refName ) ) { - model.createDependencyEdge( md, interfcDesc.getField( refName ), EdgeType.REFERENCES ); - declarationFound = true; - } - } - } - } else { - declarationFound = true; - } - owner = model.getClassDescriptor( owner.getSuperName() ); - } - - if ( owner.declaresField( refName ) ) { - - model.createDependencyEdge( md, owner.getField( refName ), EdgeType.REFERENCES ); - checkLegacyDotClassField( refName, owner, model ); - } - } - } - } - - private void checkLegacyDotClassField( String refName, ClassDescriptor owner, Model model ) { - - if ( refName.startsWith( SYNTHETIC_DOT_CLASS_FIELD_START ) ) { - FieldDescriptor fd = owner.getField( refName ); - if ( CLASS_DESC.equals( fd.getDesc() ) && fd.isSynthetic() ) { - - StringBuilder[] possibleClassNames = getPossibleClassNames( refName ); - - for ( StringBuilder possibleClassName : possibleClassNames ) { - String className = possibleClassName.toString(); - if ( model.isClassModeled( className ) ) { - ClassDescriptor cd = model.getClassDescriptor( className ); - model.createDependencyEdge( fd, cd, EdgeType.RESOLVE ); - } - } - } - } - } - - /** - * finds all possible internal class names for a synthetic static field like e.g. java.lang.Class - * class$test$simple$F$FI which is inserted in the bytecode of classes if the class version is < 0.49 and - * the .class-construct is used in the class. - * - * @param fieldName name of the synthetic field - */ - private StringBuilder[] getPossibleClassNames( String fieldName ) { - - String[] toks = fieldName.substring( 6 ).split( "\\$" ); - - StringBuilder[] possibleClassNames = new StringBuilder[ toks.length ]; - - for ( int i = 0; i < possibleClassNames.length; i++ ) { - possibleClassNames[ i ] = new StringBuilder(); - possibleClassNames[ i ].append( toks[ 0 ] ); - } - - for ( int i = 1; i < toks.length; i++ ) { - for ( int j = i - 1; j >= 0; j-- ) { - possibleClassNames[ j ].append( "$" ).append( toks[ i ] ); - } - for ( int j = 0; j < i; j++ ) { - possibleClassNames[ i ].append( "/" ).append( toks[ j + 1 ] ); - } - } - return possibleClassNames; - } } diff --git a/src/main/java/com/yworks/yshrink/core/Dfs.java b/src/main/java/com/yworks/yshrink/core/Dfs.java deleted file mode 100644 index b34109c0..00000000 --- a/src/main/java/com/yworks/yshrink/core/Dfs.java +++ /dev/null @@ -1,416 +0,0 @@ -package com.yworks.yshrink.core; - -import com.yworks.util.graph.Network; -import java.util.HashMap; -import java.util.Map; - -/** - * Framework class for depth first search (DFS) based algorithms. To write graph algorithms that are based on a depth - * first search one can extend this class and overwrite appropriate callback methods provided by this class. - * - * @author Roland Wiese (RW) - */ -public class Dfs { - - private Map edgeVisit; - - private int dfsNum; - private int compNum; - private Network network; - - private boolean directedMode; - - /** - * NodeMap that indicates the state of the nodes as they are visited by this algorithm. Possible states of a node are - * {@link #WHITE WHITE}, {@link #GRAY GRAY} and {@link #BLACK BLACK}. - */ - protected Map stateMap; - - /** - * Node state specifier. Indicates that a node was not yet visited. - */ - protected static Object WHITE = null; - - /** - * Node state specifier. Indicates that a node was already visited but has not been completed yet, i.e. it is still - * part of an active path of the dfs tree. - */ - protected static Object GRAY = new Object(); - - /** - * Node state specifier. Indicates that the node has been completed, i.e. it has been visited before and is not part - * of an active path in the dfs tree anymore. - */ - protected static Object BLACK = new Object(); - - /** - * Instantiates a new Dfs object. - */ - public Dfs() { - directedMode = false; - } - - /** - * Whether or not to interpret the edges of the graph as directed. - * By default directed mode is disabled. - * - * @param directed the directed - */ - public void setDirectedMode( final boolean directed ) { - directedMode = directed; - } - - /** - * Starts a depth first search on the given graph. The given node will be visited first. If start is - * null, this method returns silently. - * - * @param network the network - * @param start the start - */ - public void start( final Network network, final Object start ) { - if ( null == start ) return; - this.network = network; - - stateMap = new HashMap<>(); - if ( !directedMode ) { - edgeVisit = new HashMap<>(); - } - - dfsNum = 0; - compNum = 0; - - final int stackSize = Math.min( 60, network.nodesSize() + 3 ); - Stack stack = new Stack( stackSize ); - - try { - workStack( stack, start ); -// for ( NodeCursor nodeCursor = starts.nodes(); nodeCursor.ok(); nodeCursor.next() ) { -// Node node = nodeCursor.node(); -// if ( stateMap.get( node ) != BLACK ) { -// -// } -// } - } finally { - stateMap.clear(); - if ( !directedMode ) { - edgeVisit.clear(); - } - } - } - - private Object nextEdge( final Object currentNode, final Object currentEdge, final byte[] currentMode ) { - - switch ( currentMode[ 0 ] ) { - - case 0: - if ( directedMode ) { - currentMode[ 0 ] = 1; - - // return null to force finish - return network.firstOutEdge(currentNode); - } else { - Object edge = network.firstOutEdge(currentNode); - if ( edge == null ) { - edge = network.firstInEdge(currentNode); - currentMode[ 0 ] = 3; - } else { - currentMode[ 0 ] = 2; - } - return edge; - } - case 1: - // return null to force finish - return network.nextOutEdge(currentEdge); - case 2: { - Object edge = network.nextOutEdge(currentEdge); - if ( edge == null ) { - edge = network.firstInEdge(currentNode); - currentMode[ 0 ] = 3; - } - return edge; - } - case 3: - return network.nextInEdge(currentEdge); - default: - throw new InternalError(); - } - } - - private Object doNextEdge( final Object currentNode, final Object currentEdge, final byte[] currentMode ) { - - Object edge = nextEdge( currentNode, currentEdge, currentMode ); - - while ( edge != null && !doTraverse( edge ) ) { - edge = nextEdge( currentNode, edge, currentMode ); - } - - return edge; - } - - private byte[] nextState = new byte[1]; - - private void workStack( final Stack stack, final Object start ) { - nextState[ 0 ] = 0; - Object currentNode = start; - stateMap.put( currentNode, GRAY); - preVisit( currentNode, ++dfsNum ); - - { - final Object nextEdge = doNextEdge( currentNode, null, nextState ); - stack.pushState( currentNode, nextEdge, nextState[ 0 ], dfsNum ); - } - - while ( !stack.isEmpty() ) { - - Object edge = stack.peekCurrentEdge(); - nextState[ 0 ] = stack.peekIteratorState(); - - while ( edge != null ) { - - if ( directedMode || !(boolean)edgeVisit.get( edge ) ) { - final Object other; - if ( !directedMode ) { - edgeVisit.put( edge, true ); - other = network.opposite(edge, currentNode); - } else { - other = network.getTarget(edge); - } - if ( stateMap.get( other ) == null ) { - - // ! - preTraverse( edge, other, true ); - - stateMap.put( other, GRAY ); - currentNode = other; - preVisit( currentNode, ++dfsNum ); - { - nextState[ 0 ] = 0; - edge = doNextEdge( currentNode, null, nextState ); - - stack.pushState( currentNode, edge, nextState[ 0 ], dfsNum ); - } - } else { - - // ! - preTraverse( edge, other, false ); - { - edge = doNextEdge( currentNode, edge, nextState ); - - stack.updateTop( edge, nextState[ 0 ] ); - } - } - } else { - - // ! - edge = doNextEdge( currentNode, edge, nextState ); - - stack.updateTop( edge, nextState[ 0 ] ); - } - } - postVisit( currentNode, stack.peekLocalDfsNum(), ++compNum ); - stateMap.put( currentNode, BLACK ); - stack.pop(); - if ( !stack.isEmpty() ) { - final Object currentEdge = stack.peekCurrentEdge(); - postTraverse( currentEdge, currentNode ); - currentNode = stack.peekNode(); - nextState[ 0 ] = stack.peekIteratorState(); - { - final Object nextEdge = doNextEdge( currentNode, currentEdge, nextState ); - - stack.updateTop( nextEdge, nextState[ 0 ] ); - } - } - } - } - - /** - * Callback method that will be invoked whenever a formerly unvisited node gets visited the first time. The given int - * is the dfsnumber of that node. - * By default this method does nothing - * - * @param node the node - * @param dfsNumber the dfs number - */ - protected void preVisit( final Object node, final int dfsNumber ) { - } - - /** - * Callback method that will be invoked whenever a node visit has been completed. The dfs number and the completion - * number of the given node will be passed in. By default this method does nothing - * - * @param node the node - * @param dfsNumber the dfs number - * @param compNumber the comp number - */ - protected void postVisit( final Object node, final int dfsNumber, final int compNumber ) { - } - - /** - * Callback method that will be invoked if the given edge will be looked at in the search the first (and only) time. - * The given node is the node that will be visited next iff treeEdge == true. By default this method does - * nothing - * - * @param edge the edge - * @param node the node - * @param treeEdge the tree edge - * @return the boolean - */ - protected boolean preTraverse( final Object edge, final Object node, final boolean treeEdge ) { - return true; - } - - /** - * Callback method that will be invoked after the search returns from the given node. The node has been reached via - * the given edge. By default this method does nothing. - * - * @param edge the edge - * @param node the node - */ - protected void postTraverse( final Object edge, final Object node ) { - } - - /** - * Do traverse boolean. - * - * @param e the e - * @return the boolean - */ - protected boolean doTraverse( final Object e ) { - return true; - } - - /** - * The type Stack. - */ - static class Stack { - /** - * The Stack index. - */ - int stackIndex = -1; - /** - * The Iterator states. - */ - byte[] iteratorStates; - /** - * The Current edges. - */ - Object[] currentEdges; - /** - * The Local dfs nums. - */ - int[] localDfsNums; - /** - * The Nodes. - */ - Object[] nodes; - - /** - * Instantiates a new Stack. - * - * @param initialSize the initial size - */ - Stack( final int initialSize ) { - localDfsNums = new int[initialSize]; - currentEdges = new Object[initialSize]; - iteratorStates = new byte[initialSize]; - nodes = new Object[initialSize]; - } - - /** - * Is empty boolean. - * - * @return the boolean - */ - boolean isEmpty() { - return stackIndex < 0; - } - - /** - * Pop. - */ - void pop() { - stackIndex--; - } - - /** - * Peek node node. - * - * @return the node - */ - Object peekNode() { - return nodes[ stackIndex ]; - } - - /** - * Peek current edge edge. - * - * @return the edge - */ - Object peekCurrentEdge() { - return currentEdges[ stackIndex ]; - } - - /** - * Peek iterator state byte. - * - * @return the byte - */ - byte peekIteratorState() { - return iteratorStates[ stackIndex ]; - } - - /** - * Peek local dfs num int. - * - * @return the int - */ - int peekLocalDfsNum() { - return localDfsNums[ stackIndex ]; - } - - /** - * Push state int. - * - * @param node the node - * @param currentEdge the current edge - * @param iterastorState the iterastor state - * @param localDfsNum the local dfs num - * @return the int - */ - int pushState( final Object node, final Object currentEdge, final byte iterastorState, final int localDfsNum ) { - stackIndex++; - if ( stackIndex == nodes.length ) { - final int newSize = ( stackIndex + 1 ) * 2; - final Object[] newStack = new Object[newSize]; - System.arraycopy( nodes, 0, newStack, 0, nodes.length ); - this.nodes = newStack; - final Object[] newEStack = new Object[newSize]; - System.arraycopy( currentEdges, 0, newEStack, 0, currentEdges.length ); - this.currentEdges = newEStack; - final int[] newDStack = new int[newSize]; - System.arraycopy( localDfsNums, 0, newDStack, 0, localDfsNums.length ); - this.localDfsNums = newDStack; - final byte[] newStateStack = new byte[newSize]; - System.arraycopy( iteratorStates, 0, newStateStack, 0, iteratorStates.length ); - this.iteratorStates = newStateStack; - } - this.nodes[ stackIndex ] = node; - this.currentEdges[ stackIndex ] = currentEdge; - this.iteratorStates[ stackIndex ] = iterastorState; - return this.localDfsNums[ stackIndex ] = localDfsNum; - } - - /** - * Update top. - * - * @param currentEdge the current edge - * @param iteratorState the iterator state - */ - void updateTop( final Object currentEdge, final byte iteratorState ) { - this.currentEdges[ stackIndex ] = currentEdge; - this.iteratorStates[ stackIndex ] = iteratorState; - } - } -} - diff --git a/src/main/java/com/yworks/yshrink/core/DirectoryWriter.java b/src/main/java/com/yworks/yshrink/core/DirectoryWriter.java deleted file mode 100644 index 64417f86..00000000 --- a/src/main/java/com/yworks/yshrink/core/DirectoryWriter.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.yworks.yshrink.core; - -import com.yworks.util.abstractjar.impl.DirectoryWriterImpl; - -import java.io.File; -import java.io.IOException; -import java.util.jar.Manifest; - -/** - * Placeholder for a directory writer with support for digests. - * - * @author Thomas Behr - */ -class DirectoryWriter extends DirectoryWriterImpl { - DirectoryWriter( - final File outFile, final Manifest manifest - ) throws IOException { - super(outFile, manifest); - } -} diff --git a/src/main/java/com/yworks/yshrink/core/OutputVisitor.java b/src/main/java/com/yworks/yshrink/core/OutputVisitor.java deleted file mode 100644 index c0fd90f0..00000000 --- a/src/main/java/com/yworks/yshrink/core/OutputVisitor.java +++ /dev/null @@ -1,676 +0,0 @@ -package com.yworks.yshrink.core; - -import com.yworks.yshrink.model.*; -import com.yworks.logging.Logger; -import com.yworks.yshrink.util.Util; -import com.yworks.logging.XmlLogger; -import com.yworks.yguard.obf.classfile.ClassConstants; -import org.objectweb.asm.*; - -/** - * The type Output visitor. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class OutputVisitor extends ClassVisitor { - static final int OPCODES_ASM = Opcodes.ASM9; - - private ClassVisitor cv; - private Model model; - private final boolean createStubs; - - private int numObsoleteMethods = 0; - private int numObsoleteFields = 0; - - private ClassDescriptor currentClass; - - private final DoNothingAnnotationVisitor ignoreAnnotation = new DoNothingAnnotationVisitor(); - - /** - * Instantiates a new Output visitor. - * - * @param cv the cv - * @param model the model - * @param createStubs the create stubs - */ - public OutputVisitor( final ClassVisitor cv, final Model model, boolean createStubs ) { - super(OPCODES_ASM); - this.createStubs = createStubs; - this.cv = cv; - this.model = model; - } - - public void visit( final int version, final int access, final String name, final String signature, - final String superName, final String[] interfaces ) { - - currentClass = model.getClassDescriptor( name ); - if ( model.isObsolete( currentClass.getNode() ) ) { - throw new IllegalArgumentException( "Writing obsolete class: " + name ); - } - - cv.visit( version, access, name, signature, superName, interfaces ); - } - - /** - * @param source source file - * @param debug SourceDebugExtension - */ - public void visitSource( String source, String debug ) { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_SourceFile ) ) { - source = null; - } - - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_SourceDebug ) ) { - debug = null; - } - - cv.visitSource( source, debug ); - } - - public void visitOuterClass( final String owner, final String name, final String desc ) { - cv.visitOuterClass( owner, name, desc ); - } - - public AnnotationVisitor visitAnnotation( final String desc, final boolean visible ) { - - if ( visible ) { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeVisibleAnnotations ) ) { - return ignoreAnnotation; - } - } else { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeInvisibleAnnotations ) ) { - return ignoreAnnotation; - } - } - - return new OutputAnnotationVisitor( cv.visitAnnotation( desc, visible ) ); - - } - - public AnnotationVisitor visitTypeAnnotation( - final int typeRef, final TypePath typePath, final String descriptor, final boolean visible - ) { - if (visible) { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeVisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } else { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeInvisibleAnnotations)) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor(cv.visitTypeAnnotation(typeRef, typePath, descriptor, visible)); - } - - public void visitAttribute( final Attribute attr ) { - if ( currentClass.getRetainAttribute( attr.type ) ) { - cv.visitAttribute( attr ); - } - } - - public void visitInnerClass( final String name, final String outerName, final String innerName, final int access ) { - // inner classes might be externally defined ?? - if ( model.isClassModeled( name ) ) { - final ClassDescriptor cd = model.getClassDescriptor( name ); - if ( !model.isObsolete( cd.getNode() ) ) { - cv.visitInnerClass( name, outerName, innerName, access ); - } - } - } - - public void visitNestHost( final String nestHost ) { - final ClassDescriptor cd = model.getClassDescriptor(nestHost); - if (!model.isObsolete(cd.getNode())) { - cv.visitNestHost(nestHost); - } - } - - public void visitNestMember( final String nestMember ) { - final ClassDescriptor cd = model.getClassDescriptor(nestMember); - if (!model.isObsolete(cd.getNode()) && currentClass.getHasNestMembers()) { - cv.visitNestMember(nestMember); - } - } - - public FieldVisitor visitField( final int access, final String name, final String desc, final String signature, - final Object value ) { - - final FieldDescriptor fd = currentClass.getField( name ); - if ( model.isObsolete( fd.getNode() ) ) { - Logger.shrinkLog( - "\t\t" ); - numObsoleteFields++; - return null; - } else { - return new OutputFieldVisitor(cv.visitField(access, name, desc, signature, value)); - } - } - - public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, - final String[] exceptions ) { - - final MethodDescriptor md = currentClass.getMethod( name, desc ); - if ( model.isObsolete( md.getNode() ) ) { - - if ( ! model.isStubNeeded( md.getNode() ) ) { - numObsoleteMethods++; - Logger.shrinkLog( "\t\t" ); - } - - if ( createStubs || model.isStubNeeded( md.getNode() ) ) { - boolean visitStub = !md.hasFlag(Opcodes.ACC_ABSTRACT); - return new StubOutputMethodVisitor( cv.visitMethod( access, name, desc, signature, exceptions ), visitStub ); - } else { - return null; - } - } else { - return new OutputMethodVisitor( cv.visitMethod( access, name, desc, signature, exceptions ) ); - } - } - - private void visitStub( MethodVisitor mv ) { - mv.visitCode(); - mv.visitTypeInsn( Opcodes.NEW, "java/lang/InternalError" ); - mv.visitInsn( Opcodes.DUP ); - mv.visitLdcInsn( "Badly shrinked" ); - mv.visitMethodInsn( Opcodes.INVOKESPECIAL, "java/lang/InternalError", "", "(Ljava/lang/String;)V", currentClass.isInterface() ); - mv.visitInsn( Opcodes.ATHROW ); - mv.visitMaxs( 3, 1 ); - } - - public void visitEnd() { - cv.visitEnd(); - } - - /** - * Gets num obsolete methods. - * - * @return the num obsolete methods - */ - public int getNumObsoleteMethods() { - return numObsoleteMethods; - } - - /** - * Gets num obsolete fields. - * - * @return the num obsolete fields - */ - public int getNumObsoleteFields() { - return numObsoleteFields; - } - - - /** - * The type Output method visitor. - */ - class OutputMethodVisitor extends MethodVisitor { - - private MethodVisitor delegate; - - /** - * Instantiates a new Output method visitor. - * - * @param delegate the delegate - */ - public OutputMethodVisitor( MethodVisitor delegate ) { - super(OPCODES_ASM); - this.delegate = delegate; - } - - // visit default value in Annotation interface - public AnnotationVisitor visitAnnotationDefault() { - return delegate.visitAnnotationDefault(); - } - - public AnnotationVisitor visitAnnotation( String desc, boolean visible ) { - if ( visible ) { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeVisibleAnnotations ) ) { - return ignoreAnnotation; - } - } else { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeInvisibleAnnotations ) ) { - return ignoreAnnotation; - } - } - - return new OutputAnnotationVisitor( delegate.visitAnnotation( desc, visible ) ); - } - - public AnnotationVisitor visitParameterAnnotation( int parameter, String desc, boolean visible ) { - if ( visible ) { - if ( !currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeVisibleParameterAnnotations ) ) { - return ignoreAnnotation; - } - } else { - if ( !currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations ) ) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor( delegate.visitParameterAnnotation( parameter, desc, visible ) ); - } - - public AnnotationVisitor visitInsnAnnotation( - final int typeRef, final TypePath typePath, final String descriptor, final boolean visible - ) { - if (visible) { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeVisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } else { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor(delegate.visitInsnAnnotation(typeRef, typePath, descriptor, visible)); - } - - public AnnotationVisitor visitLocalVariableAnnotation( - final int typeRef, final TypePath typePath, final Label[] start, final Label[] end, final int[] index, final String descriptor, final boolean visible - ) { - if (visible) { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeVisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } else { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor(delegate.visitLocalVariableAnnotation(typeRef, typePath,start, end, index, descriptor, visible)); - } - - public AnnotationVisitor visitTypeAnnotation( - final int typeRef, final TypePath typePath, final String descriptor, final boolean visible - ) { - if (visible) { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeVisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } else { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor(delegate.visitTypeAnnotation(typeRef, typePath, descriptor, visible)); - } - - @Override - public AnnotationVisitor visitTryCatchAnnotation( - final int typeRef, final TypePath typePath, final String descriptor, final boolean visible - ) { - if (visible) { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeVisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } else { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor(delegate.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible)); - } - - public void visitAttribute( Attribute attr ) { - if ( currentClass.getRetainAttribute( attr.type ) ) { - delegate.visitAttribute( attr ); - } - } - - public void visitCode() { - delegate.visitCode(); - } - - // asm 3.1 - public void visitFrame(int i, int i1, Object[] objects, int i2, Object[] objects1) { - delegate.visitFrame(i, i1, objects, i2, objects1); - } - - - public void visitInsn( int opcode ) { - delegate.visitInsn( opcode ); - } - - public void visitIntInsn( int opcode, int operand ) { - delegate.visitIntInsn( opcode, operand ); - } - - public void visitVarInsn( int opcode, int var ) { - delegate.visitVarInsn( opcode, var ); - } - - public void visitTypeInsn( int opcode, String desc ) { - delegate.visitTypeInsn( opcode, desc ); - } - - public void visitFieldInsn( int opcode, String owner, String name, String desc ) { - delegate.visitFieldInsn( opcode, owner, name, desc ); - } - - public void visitMethodInsn( int opcode, String owner, String name, String desc, boolean itf ) { - delegate.visitMethodInsn( opcode, owner, name, desc, itf ); - } - - public void visitJumpInsn( int opcode, Label label ) { - delegate.visitJumpInsn( opcode, label ); - } - - public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { - delegate.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); - } - - public void visitLabel( Label label ) { - delegate.visitLabel( label ); - } - - public void visitLdcInsn( Object cst ) { - delegate.visitLdcInsn( cst ); - } - - public void visitIincInsn( int var, int increment ) { - delegate.visitIincInsn( var, increment ); - } - - public void visitTableSwitchInsn( int min, int max, Label dflt, Label... labels ) { - delegate.visitTableSwitchInsn( min, max, dflt, labels ); - } - - public void visitLookupSwitchInsn( Label dflt, int[] keys, Label[] labels ) { - delegate.visitLookupSwitchInsn( dflt, keys, labels ); - } - - public void visitMultiANewArrayInsn( String desc, int dims ) { - delegate.visitMultiANewArrayInsn( desc, dims ); - } - - public void visitTryCatchBlock( Label start, Label end, Label handler, String type ) { - delegate.visitTryCatchBlock( start, end, handler, type ); - } - - public void visitLocalVariable( String name, String desc, String signature, Label start, Label end, int index ) { - if ( null != signature ) { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_LocalVariableTypeTable ) ) { - return; - } - } else { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_LocalVariableTable ) ) { - return; - } - } - delegate.visitLocalVariable( name, desc, signature, start, end, index ); - } - - public void visitLineNumber( int line, Label start ) { - if ( currentClass.getRetainAttribute( ClassConstants.ATTR_LineNumberTable ) ) { - delegate.visitLineNumber( line, start ); - } - } - - public void visitMaxs( int maxStack, int maxLocals ) { - delegate.visitMaxs( maxStack, maxLocals ); - } - - public void visitEnd() { - delegate.visitEnd(); - } - } - - /** - * The type Output field visitor. - */ - class OutputFieldVisitor extends FieldVisitor { - - private final FieldVisitor delegate; - - /** - * Instantiates a new Output field visitor. - * - * @param delegate the delegate - */ - public OutputFieldVisitor(FieldVisitor delegate) { - super(OPCODES_ASM); - this.delegate = delegate; - } - - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if ( visible ) { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeVisibleAnnotations ) ) { - return ignoreAnnotation; - } - } else { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeInvisibleAnnotations ) ) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor( delegate.visitAnnotation( desc, visible ) ); - } - - public AnnotationVisitor visitTypeAnnotation( - final int typeRef, final TypePath typePath, final String descriptor, final boolean visible - ) { - if (visible) { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeVisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } else { - if (!currentClass.getRetainAttribute(ClassConstants.ATTR_RuntimeInvisibleTypeAnnotations)) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor(delegate.visitTypeAnnotation(typeRef, typePath, descriptor, visible)); - } - - public void visitAttribute(Attribute attribute) { - if ( currentClass.getRetainAttribute( attribute.type ) ) { - delegate.visitAttribute( attribute ); - } - } - - public void visitEnd() { - delegate.visitEnd(); - } - } - - /** - * The type Stub output method visitor. - */ - class StubOutputMethodVisitor extends MethodVisitor { - - private MethodVisitor delegate; - private final boolean visitStub; - - /** - * Instantiates a new Stub output method visitor. - * - * @param delegate the delegate - * @param visitStub the visit stub - */ - public StubOutputMethodVisitor(MethodVisitor delegate, boolean visitStub) { - super(OPCODES_ASM); - this.delegate = delegate; - this.visitStub = visitStub; - } - - // visit default value in Annotation interface - public AnnotationVisitor visitAnnotationDefault() { - return delegate.visitAnnotationDefault(); - } - - public AnnotationVisitor visitAnnotation( String desc, boolean visible ) { - - if ( visible ) { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeVisibleAnnotations ) ) { - return ignoreAnnotation; - } - } else { - if ( ! currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeInvisibleAnnotations ) ) { - return ignoreAnnotation; - } - } - - return new OutputAnnotationVisitor( delegate.visitAnnotation( desc, visible ) ); - } - - public AnnotationVisitor visitParameterAnnotation( int parameter, String desc, boolean visible ) { - if ( visible ) { - if ( !currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeVisibleParameterAnnotations ) ) { - return ignoreAnnotation; - } - } else { - if ( !currentClass.getRetainAttribute( ClassConstants.ATTR_RuntimeInvisibleParameterAnnotations ) ) { - return ignoreAnnotation; - } - } - return new OutputAnnotationVisitor( delegate.visitParameterAnnotation( parameter, desc, visible ) ); - } - - public void visitAttribute( Attribute attr ) { - if ( currentClass.getRetainAttribute( attr.type ) ) { - delegate.visitAttribute( attr ); - } - } - - public void visitCode() { - } - - public void visitFrame(int i, int i1, Object[] objects, int i2, Object[] objects1) { - } - - public void visitInsn( int opcode ) { - } - - public void visitIntInsn( int opcode, int operand ) { - } - - public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { - } - - public void visitVarInsn( int opcode, int var ) { - } - - public void visitTypeInsn( int opcode, String desc ) { - } - - public void visitFieldInsn( int opcode, String owner, String name, String desc ) { - } - - public void visitMethodInsn( int opcode, String owner, String name, String desc, boolean itf ) { - } - - public void visitJumpInsn( int opcode, Label label ) { - } - - public void visitLabel( Label label ) { - } - - public void visitLdcInsn( Object cst ) { - } - - public void visitIincInsn( int var, int increment ) { - } - - public void visitTableSwitchInsn( int min, int max, Label dflt, Label... labels ) { - } - - public void visitLookupSwitchInsn( Label dflt, int[] keys, Label[] labels ) { - } - - public void visitMultiANewArrayInsn( String desc, int dims ) { - } - - public void visitTryCatchBlock( Label start, Label end, Label handler, String type ) { - } - - public void visitLocalVariable( String name, String desc, String signature, Label start, Label end, int index ) { - } - - public void visitLineNumber( int line, Label start ) { - } - - public void visitMaxs( int maxStack, int maxLocals ) { - } - - public void visitEnd() { - if (visitStub) { - visitStub(delegate); - } - delegate.visitEnd(); - } - } - - /** - * can't return null where returntype is AnnotationVisitor.. - */ - static class DoNothingAnnotationVisitor extends AnnotationVisitor { - - /** - * Instantiates a new Do nothing annotation visitor. - */ - public DoNothingAnnotationVisitor() { - super(OPCODES_ASM); - } - - public void visit( String name, Object value ) { - } - - public void visitEnum( String name, String desc, String value ) { - } - - public AnnotationVisitor visitAnnotation( String name, String desc ) { - return this; - } - - public AnnotationVisitor visitArray( String name ) { - return this; - } - - public void visitEnd() { - } - } - - - /** - * currently just delegates all methods to delegate. - */ - class OutputAnnotationVisitor extends AnnotationVisitor { - - - /** - * The Delegate. - */ - AnnotationVisitor delegate; - - /** - * Instantiates a new Output annotation visitor. - * - * @param delegate the delegate - */ - public OutputAnnotationVisitor( AnnotationVisitor delegate ) { - super(OPCODES_ASM); - this.delegate = delegate; - } - - public void visit( String name, Object value ) { - delegate.visit( name, value ); - } - - public void visitEnum( String name, String desc, String value ) { - delegate.visitEnum( name, desc, value ); - } - - public AnnotationVisitor visitAnnotation( String name, String desc ) { - return new OutputAnnotationVisitor( delegate.visitAnnotation( name, desc ) ); - } - - public AnnotationVisitor visitArray( String name ) { - return new OutputAnnotationVisitor( delegate.visitArray( name ) ); - } - - public void visitEnd() { - delegate.visitEnd(); - } - } - -} diff --git a/src/main/java/com/yworks/yshrink/core/Shrinker.java b/src/main/java/com/yworks/yshrink/core/Shrinker.java deleted file mode 100644 index eeb5c160..00000000 --- a/src/main/java/com/yworks/yshrink/core/Shrinker.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.yworks.yshrink.core; - -import com.yworks.yshrink.model.AbstractDescriptor; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.EdgeType; -import com.yworks.yshrink.model.MethodDescriptor; -import com.yworks.yshrink.model.Model; -import com.yworks.yshrink.model.NodeType; -import com.yworks.util.graph.Network; -import org.objectweb.asm.Opcodes; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.HashMap; - -/** - * The type Shrinker. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class Shrinker { - - //private Model model; - - /** - * Shrink. - * - * @param model the model - */ - public void shrink( final Model model ) { - //this.model = model; - - final ShrinkDfs shrinkDfs = new ShrinkDfs( model ); - shrinkDfs.setDirectedMode( true ); - - // initially mark all nodes OBSOLETE - Iterator nodeIterator = model.getNetwork().nodes(); - while (nodeIterator.hasNext()) { - Object node = nodeIterator.next(); - model.markObsolete( node ); - } - - shrinkDfs.init( model.getEntryPointNode() ); - - int numInstantiated = -1; - while ( shrinkDfs.numInstantiated > numInstantiated ) { - numInstantiated = shrinkDfs.numInstantiated; - shrinkDfs.nextRound(); - } - - shrinkDfs.markReachableNodes(); - } - - private class ShrinkDfs extends Dfs { - - private Model model; - private Network network; - private Object entryPointNode; - private Map instanceMap; - private int numInstantiated = 0; - private int round = 0; - private int numSkipped = 0; - - private static final int EXPLORE_MODE = 0; - private static final int RESULT_MODE = 1; - - private int mode = EXPLORE_MODE; - - /** - * Instantiates a new Shrink dfs. - * - * @param model the model - */ - ShrinkDfs( final Model model ) { - this.model = model; - this.network = model.getNetwork(); - } - - /** - * Init. - * - * @param entryPointNode the entry point node - */ - public void init( final Object entryPointNode ) { - - this.entryPointNode = entryPointNode; - - round = 0; - if ( instanceMap == null ) { - this.instanceMap = new HashMap<>(); - } - Iterator nodeIterator = network.nodes(); - while (nodeIterator.hasNext()) { - Object n = nodeIterator.next(); - instanceMap.put( n, -1 ); - } - } - - /** - * Next round int. - * - * @return the int - */ - protected int nextRound() { - round++; - numSkipped = 0; - numInstantiated = 0; - super.start( network, entryPointNode ); - return numInstantiated; - } - - @Override - protected void postVisit( final Object node, final int i, final int j ) { - - if ( mode == EXPLORE_MODE ) { - if ( NodeType.isNewNode( model.getNodeType( node ) ) ) { - - final Object classNode = model.getClassNode( node ); - - instanceMap.put( classNode, round ); - numInstantiated++; - } - } - } - - @Override - protected void preVisit( final Object node, final int dfsNumber ) { - - if ( mode == RESULT_MODE ) { - - model.markNotObsolete( node ); - } - } - - /** - * Mark reachable nodes. - */ - protected void markReachableNodes() { - int oldMode = mode; - mode = RESULT_MODE; - super.start( network, this.entryPointNode ); - mode = oldMode; - } - - @Override - protected boolean doTraverse( final Object edge ) { - - boolean allowed = false; - - // TODO use NodeType - final Object target = network.getTarget(edge); - - // class, field node: allow always - if ( !NodeType.isMethodNode( model.getNodeType( target ) ) ) { - - allowed = true; - } else { - - if ( ! (model.getDependencyType( edge ).equals( EdgeType.RESOLVE ) || - model.getDependencyType( edge ).equals( EdgeType.ENCLOSE ))) { - - final AbstractDescriptor targetDescriptor = model.getDescriptor( target ); - final MethodDescriptor targetMethod = (MethodDescriptor) targetDescriptor; - final Object classNode = model.getClassNode( target ); - final ClassDescriptor targetClass = (ClassDescriptor) model.getDescriptor( classNode ); - - allowed = allowed || targetMethod.isStatic(); - - // default method - allowed = allowed || targetMethod.hasFlag(Opcodes.ACC_PUBLIC) && !targetMethod.hasFlag(Opcodes.ACC_ABSTRACT); - - allowed = allowed || targetClass.isAnnotation(); - - allowed = allowed || ( model.getDependencyType( edge ).equals( EdgeType.SUPER ) ); - - allowed = allowed || - ( NodeType.isNewNode( model.getNodeType( target ) ) ); - - allowed = allowed || ( Model.CONSTRUCTOR_NAME.equals( targetMethod.getName() ) ); - - allowed = allowed || ( targetMethod.isPrivate() ); - - allowed = allowed || wasClassInstantiated( edge ); - - allowed = allowed || isMethodNeeded( targetClass, targetMethod ); - - allowed = allowed || model.getDependencyType( edge ).equals( EdgeType.INVOKEDYNAMIC ); - - // resolve edge: mark target stub as needed - } else if ( mode == RESULT_MODE ) { - model.markStubNeeded( target ); - } - } - - return allowed; - } - - /** - * A Method is needed if a descendant class does not override the method and the descendant class is instantiated. - * TODO improve alorithm - */ - private boolean isMethodNeeded( ClassDescriptor cd, MethodDescriptor md ) { - - List descendants = new ArrayList( 5 ); - model.getInternalDescendants( cd, descendants ); - - for ( ClassDescriptor descendant : descendants ) { - - if ( ( !descendant.implementsMethod( md.getName(), md.getDesc() ) ) && - (int) instanceMap.get( descendant.getNode() ) >= ( round - 1 ) ) { - return true; - } - } - return false; - } - - private boolean wasClassInstantiated( final Object edge ) { - - final Object targetNode = network.getTarget(edge); - final Object classNode = model.getClassNode( targetNode ); - if ( (int) instanceMap.get( classNode ) >= ( round - 1 ) ) { - return true; - } else { - - numSkipped++; - - return false; - } - } - - /** - * Gets num skipped. - * - * @return the num skipped - */ - protected int getNumSkipped() { - return numSkipped; - } - } -} diff --git a/src/main/java/com/yworks/yshrink/core/URLCpResolver.java b/src/main/java/com/yworks/yshrink/core/URLCpResolver.java deleted file mode 100644 index 2bf04e59..00000000 --- a/src/main/java/com/yworks/yshrink/core/URLCpResolver.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.yworks.yshrink.core; - -import java.net.URL; -import java.net.URLClassLoader; - -/** - * The type Url cp resolver. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class URLCpResolver implements ClassResolver { - - /** - * The Url class loader. - */ - URLClassLoader urlClassLoader; - - /** - * Instantiates a new Url cp resolver. - * - * @param urls the urls - */ - public URLCpResolver( final URL[] urls ) { - urlClassLoader = URLClassLoader.newInstance( urls, ClassLoader.getSystemClassLoader() ); - } - - public Class resolve( final String className ) throws ClassNotFoundException { - try { - return Class.forName( className, false, urlClassLoader ); - } catch ( NoClassDefFoundError ncdfe ) { - String message = ncdfe.getMessage(); - if ( message == null || message.equals( className ) ) { - message = className; - } else { - message = message + "[" + className + "]"; - } - throw new ClassNotFoundException( message, ncdfe ); - } catch ( LinkageError le ) { - throw new ClassNotFoundException( className, le ); - } - } - - @Override - public void close() throws Exception { - urlClassLoader.close(); - } -} diff --git a/src/main/java/com/yworks/yshrink/core/Writer.java b/src/main/java/com/yworks/yshrink/core/Writer.java deleted file mode 100644 index 263b73ad..00000000 --- a/src/main/java/com/yworks/yshrink/core/Writer.java +++ /dev/null @@ -1,378 +0,0 @@ -package com.yworks.yshrink.core; - -import com.yworks.common.ResourcePolicy; -import com.yworks.common.ShrinkBag; -import com.yworks.logging.Logger; -import com.yworks.util.Version; -import com.yworks.util.abstractjar.Archive; -import com.yworks.util.abstractjar.ArchiveWriter; -import com.yworks.util.abstractjar.Factory; -import com.yworks.util.abstractjar.StreamProvider; -import com.yworks.yshrink.model.ClassDescriptor; -import com.yworks.yshrink.model.Model; -import com.yworks.yshrink.util.Util; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassWriter; - -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.text.NumberFormat; -import java.util.HashSet; -import java.util.Set; -import java.util.jar.Attributes; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; - -/** - * The type Writer. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class Writer { - - private static final String MANIFEST_FILENAME = "META-INF/MANIFEST.MF"; - private static final String SIGNATURE_FILE_PREFIX = "META-INF/"; - private static final String SIGNATURE_FILE_SUFFIX = ".SF"; - - private final boolean createStubs; - private final MessageDigest[] digests; - - /** - * Instantiates a new Writer. - * - * @param createStubs the create stubs - * @param digestNamesStr the digest names str - */ - public Writer( boolean createStubs, String digestNamesStr ) { - this.createStubs = createStubs; - - String[] digestNames = digestNamesStr.trim().equalsIgnoreCase( "none" ) - ? new String[ 0 ] : digestNamesStr.split( "," ); - - for ( int i = 0; i < digestNames.length; i++ ) { - digestNames[ i ] = digestNames[ i ].trim(); - } - - digests = new MessageDigest[ digestNames.length ]; - - for ( int i = digestNames.length - 1; i >= 0; i-- ) { - try { - digests[ i ] = MessageDigest.getInstance( digestNames[ i ] ); - } catch ( NoSuchAlgorithmException e ) { - Logger.err( "Unknwon digest algorithm: " + digestNames[ i ] ); - digests[ i ] = null; - } - } - } - - public void write( Model model, ShrinkBag bag ) throws IOException { - - File in = bag.getIn(); - File out = bag.getOut(); - - Logger.log( "writing shrinked " + in + " to " + out + "." ); - - Logger.shrinkLog( "" ); - - long inLength = in.length(); - - StreamProvider provider = Factory.newStreamProvider( in ); - - if ( !out.exists() ) out.createNewFile(); - - final Manifest newManifest = getManifest( in ); - final ArchiveWriter writer = newArchiveCreator(out, newManifest ); - - int numClasses = 0; - int numObsoleteClasses = 0; - int numObsoleteMethods = 0; - int numObsoleteFields = 0; - int numRemovedResources = 0; - - Set nonEmptyDirs = new HashSet( 5 ); - - Logger.shrinkLog( "\t" ); - - for (DataInputStream stream = provider.getNextClassEntryStream(); - stream != null; - stream = provider.getNextClassEntryStream()) { - - String entryName = provider.getCurrentEntryName(); - - numClasses++; - - ClassDescriptor cd = model.getClassDescriptor( - entryName.substring( 0, entryName.lastIndexOf( ".class" ) ) ); - boolean obsolete = model.isObsolete( cd.getNode() ); - - if ( !obsolete ) { - - nonEmptyDirs.add( provider.getCurrentDir() ); - - // asm 3.x - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); - - // asm 2.x - // ClassWriter cw = new ClassWriter(true); - - OutputVisitor outputVisitor = new OutputVisitor( cw, model, createStubs ); - ClassReader cr = new ClassReader( stream ); - - // asm 3.x - cr.accept( outputVisitor,0); - - // asm 2.x - // cr.accept( outputVisitor,false); - - numObsoleteMethods += outputVisitor.getNumObsoleteMethods(); - numObsoleteFields += outputVisitor.getNumObsoleteFields(); - - byte[] modifiedClass = cw.toByteArray(); - writer.addFile(entryName, modifiedClass ); - } else { - newManifest.getEntries().remove( entryName ); - numObsoleteClasses++; - Logger.shrinkLog( "\t\t" ); - } - - close(stream); - } - - Logger.shrinkLog( "\t" ); - Logger.shrinkLog( "\t" ); - - ResourcePolicy resourcePolicy = bag.getResources(); - - if ( ! resourcePolicy.equals( ResourcePolicy.NONE ) ) { - - provider.reset(); - - for (DataInputStream stream = provider.getNextResourceEntryStream(); - stream != null; - stream = provider.getNextResourceEntryStream()) { - String entryName = provider.getCurrentEntryName(); - - if ( !resourcePolicy.equals( ResourcePolicy.NONE ) && - ( resourcePolicy.equals( ResourcePolicy.COPY ) || - ( resourcePolicy.equals( ResourcePolicy.AUTO ) && - nonEmptyDirs.contains( provider.getCurrentDir() ) ) ) ) { - - copyResource( entryName, provider, stream, writer ); - } else { - numRemovedResources++; - Logger.shrinkLog( - "\t" ); - } - - close(stream); - } - } - - try { - provider.close(); - } catch (Exception ex) { - // ignore - } - - Logger.shrinkLog( "\t" ); - - writer.close(); - - long outLength = out.length(); - - NumberFormat nf = NumberFormat.getPercentInstance(); - nf.setMinimumFractionDigits( 2 ); - String percent = nf.format( 1 - ( (double) outLength / (double) inLength ) ); - - Logger.log( "\tshrinked " + in + " BY " + percent + "." ); - Logger.log( "\tsize before: " + inLength / 1024 + " KB, size after: " + outLength / 1024 + " KB." ); - Logger.log( - "\tremoved " + numObsoleteClasses + " classes, " + numObsoleteMethods + " methods, " + numObsoleteFields + " fields, " + numRemovedResources + " resources." ); - Logger.log( "\t" + ( numClasses - numObsoleteClasses ) + " classes remaining of " + numClasses + " total." ); - - Logger.shrinkLog( "" ); - } - - private static void close( final InputStream is ) { - try { - is.close(); - } catch (Exception ex) { - // ignore - } - } - - private void copyResource( String entryName, StreamProvider jarStreamProvider, DataInputStream stream, - ArchiveWriter writer ) throws IOException { - - // don't copy manifest/signature files. - if ( !entryName.equals( MANIFEST_FILENAME ) && - !( entryName.endsWith( SIGNATURE_FILE_SUFFIX ) && - entryName.startsWith( SIGNATURE_FILE_PREFIX ) ) ) { - - int entrySize = (int) jarStreamProvider.getCurrentEntry().getSize(); - if ( -1 != entrySize ) { - byte[] data = new byte[ entrySize ]; - stream.readFully( data ); - writer.addFile( entryName, data ); - } - } - } - - - private ArchiveWriter newArchiveCreator( - final File out, final Manifest manifest - ) throws IOException{ - return out.isDirectory() - ? new DirectoryWriter( out, manifest) // TODO: support digests - : new JarWriter( out, manifest ); - } - - private static Manifest getManifest( final File file ) throws IOException { - final Archive inJar = Factory.newArchive(file); - - final Manifest oldManifest = inJar.getManifest(); - final Manifest newManifest = - oldManifest != null ? new Manifest(oldManifest) : new Manifest(); - - try { - inJar.close(); - } catch (IOException ioe) { - // ignore - } - - return newManifest; - } - - - private class JarWriter extends ArchiveWriter { - - private Set directoriesWritten = new HashSet(); - private final FileOutputStream fos; - private final JarOutputStream jos; - - private Manifest manifest; - - public JarWriter( File outFile, Manifest manifest ) throws IOException { - super(manifest); - - this.manifest = manifest; - - fos = new FileOutputStream( outFile ); - - jos = new JarOutputStream( - new BufferedOutputStream( - fos ) ); - } - - private void addDigests( String entryName ) { - - Attributes oldEntryAttributes = this.manifest.getAttributes(entryName ); - Attributes newEntryAttributes = new Attributes( digests.length + 1 ); - - if ( null != oldEntryAttributes ) { - Set keys = oldEntryAttributes.keySet(); - for ( Object key : keys ) { - if ( ( (Attributes.Name) key ).toString().indexOf( "Digest" ) == -1 ) { - newEntryAttributes.put( key, oldEntryAttributes.get( key ) ); - } - } - } - - StringBuffer digestsList = new StringBuffer(); - for (int i = 0; i < digests.length; i++) { - MessageDigest digest = digests[i]; - - if (null != digest) { - - String digestKey = digest.getAlgorithm() + "-Digest"; - digestsList.append(digest.getAlgorithm()); - if (i < digests.length - 1){ - digestsList.append(", "); - } - - String digestVal = Util.toBase64(digest.digest()); - - newEntryAttributes.putValue(digestKey, digestVal); - } - } - - newEntryAttributes.putValue("Digest-Algorithms", digestsList.toString()); - - this.manifest.getEntries().put( entryName, newEntryAttributes ); - } - - @Override - public void setComment( final String comment ) { - jos.setComment(comment); - } - - @Override - public void addFile( final String fileName, final byte[] data ) throws IOException { - - JarEntry outEntry = new JarEntry( fileName ); - addDirectory( fileName ); - jos.putNextEntry( outEntry ); - jos.write( data ); - jos.closeEntry(); - - calcDigests( data ); - - addDigests( fileName ); - } - - private void calcDigests( final byte[] data ) { - - for ( int i = digests.length - 1; i >= 0; i-- ) { - if ( null != digests[ i ] ) { - digests[ i ].reset(); - digests[ i ].update( data ); - } - } - } - - @Override - public void addDirectory( final String fileName ) throws IOException { - int index = 0; - while ( ( index = fileName.indexOf( "/", index + 1 ) ) >= 0 ) { - String directory = fileName.substring( 0, index + 1 ); - if ( !directoriesWritten.contains( directory ) ) { - directoriesWritten.add( directory ); - JarEntry directoryEntry = new JarEntry(directory ); - jos.putNextEntry( directoryEntry ); - jos.closeEntry(); - } - } - } - - @Override - public void close() throws IOException { - - finishManifest(); - - if ( jos != null ) { - jos.finish(); - jos.close(); - } - if ( fos != null ) { - fos.close(); - } - } - - private void finishManifest() throws IOException { - manifest.getMainAttributes().putValue( "Created-by", - "yGuard Bytecode Obfuscator: Shrinker " + Version.getVersion() ); - - addDirectory( MANIFEST_FILENAME ); - jos.putNextEntry( new JarEntry( MANIFEST_FILENAME ) ); - this.manifest.write( jos ); - jos.closeEntry(); - } - } -} diff --git a/src/main/java/com/yworks/yshrink/model/Model.java b/src/main/java/com/yworks/yshrink/model/Model.java index a63fdf45..8b546b30 100644 --- a/src/main/java/com/yworks/yshrink/model/Model.java +++ b/src/main/java/com/yworks/yshrink/model/Model.java @@ -133,15 +133,6 @@ public Model( Network network ) { node2Type.put( entryPointNode, NodeType.ENTRYPOINT ); } - /** - * Gets entry point node. - * - * @return the entry point node - */ - public Object getEntryPointNode() { - return entryPointNode; - } - /** * Is class modeled boolean. * @@ -311,71 +302,6 @@ public ClassDescriptor getClassDescriptor( final String className ) { } } - /** - * Gets descriptor. - * - * @param n the n - * @return the descriptor - */ - public AbstractDescriptor getDescriptor( final Object n ) { - return (AbstractDescriptor) node2Descriptor.get( n ); - } - - /** - * Gets class node. - * - * @param memberNode the member node - * @return the class node - */ - public Object getClassNode( final Object memberNode ) { - - if ( getDescriptor( memberNode ) instanceof ClassDescriptor ) { - throw new IllegalArgumentException( "Node " + memberNode + " is a classNode " ); - } - - Iterator outEdgesIterator = network.outEdges(memberNode); - while (outEdgesIterator.hasNext()) { - Object e = outEdgesIterator.next(); - if ( getDependencyType( e ).equals( EdgeType.MEMBER_OF ) ) { - return network.getTarget(e); - } - } - - throw new RuntimeException( "Node " + memberNode + " is homeless." ); - } - - /** - * Gets dependency type. - * - * @param e the e - * @return the dependency type - */ - public EdgeType getDependencyType( final Object e ) { - return (EdgeType) dependencyTypes.get( e ); - } - - /** - * retrieve all implementing classes of cd. - * - * @param cd the cd - * @return List of ClassDescriptors containing all classes that implement cd - */ - public Set getAllImplementingClasses( final ClassDescriptor cd ) { - Set ret = null; - - Iterator inEdgesIterator = network.inEdges(cd.getNode()); - while (inEdgesIterator.hasNext()) { - Object e = inEdgesIterator.next(); - if ( dependencyTypes.get( e ).equals( EdgeType.IMPLEMENTS ) ) { - if ( ret == null ) ret = new HashSet(); - final ClassDescriptor subClass = (ClassDescriptor) node2Descriptor.get( network.getSource(e) ); - ret.add( subClass ); - } - } - - return ret; - } - /** * Gets all implemented interfaces. * @@ -444,279 +370,6 @@ public void getAllAncestorClasses( final String className, } } - /** - * Gets all internal ancestor entrypoint methods. - * - * @param className the class name - * @param methods the methods - */ - public void getAllInternalAncestorEntrypointMethods( final String className, - final List methods ) { - - if ( null == className || ! isClassModeled( className ) ) { - return; - } - - ClassDescriptor cd = getClassDescriptor( className ); - - Collection classMethods = cd.getMethods(); - for ( MethodDescriptor md : classMethods ) { - if ( md.isEntryPoint() ) { - methods.add( md ); - } - } - - if ( ! ( cd.isInterface() && "java/lang/Object".equals( cd.getSuperName() ) ) ) { - getAllInternalAncestorEntrypointMethods( cd.getSuperName(), methods ); - } - String[] interfaces = cd.getInterfaces(); - for ( String interfc : interfaces ) { - getAllInternalAncestorEntrypointMethods( interfc, methods ); - } - } - - - /** - * Gets all external ancestor methods. - * - * @param className the class name - * @param methods the methods - * @return the all external ancestor methods - */ - public boolean getAllExternalAncestorMethods( final String className, final List methods ) { - - boolean r = true; - - if ( null == className ) { - return true; - } - - if ( isClassModeled( className ) ) { - - ClassDescriptor cd = getClassDescriptor( className ); - if ( ! ( cd.isInterface() && "java/lang/Object".equals( cd.getSuperName() ) ) ) { - r &= getAllExternalAncestorMethods( cd.getSuperName(), methods ); - } - String[] interfaces = cd.getInterfaces(); - for ( String interfc : interfaces ) { - r &= getAllExternalAncestorMethods( interfc, methods ); - } - } else { - - Class clazz = resolve( className ); - - if ( null != clazz ) { - - // add all methods - Method[] clazzMethods = clazz.getDeclaredMethods(); - - for ( Method method : clazzMethods ) { - methods.add( method ); - } - - // collect superclass methods - Class superClass = clazz.getSuperclass(); - if ( null != superClass ) { - r &= getAllExternalAncestorMethods( superClass.getName(), methods ); - } - - // collect interface methods - Class[] interfaces = clazz.getInterfaces(); - - for ( Class interfc : interfaces ) { - r &= getAllExternalAncestorMethods( interfc.getName(), methods ); - } - } else { - return false; - } - } - return r; - } - - /** - * collects all subclasses of cd - * - * @param cd the cd - * @param descendants the descendants - */ - public void getInternalDescendants( final ClassDescriptor cd, final List descendants ) { - Iterator inEdgesIterator = network.inEdges(cd.getNode()); - while (inEdgesIterator.hasNext()) { - Object e = inEdgesIterator.next(); - if ( dependencyTypes.get( e ).equals( EdgeType.EXTENDS ) ) { - final ClassDescriptor subClass = (ClassDescriptor) node2Descriptor.get( network.getSource(e) ); - descendants.add( subClass ); - getInternalDescendants( subClass, descendants ); - } - } - } - - private boolean isMethodDefinedInExternalInterface( final ClassDescriptor origClass, final MethodDescriptor md ) { - - boolean found = false; - - String[] interfaces = origClass.getInterfaces(); - for ( String interfc : interfaces ) { - if ( ! isClassModeled( interfc ) ) { - Class clazz = resolve( interfc ); - if ( null != clazz ) { - found = found || containsNonPrivateMethod( clazz, md ); - for ( Class clazzz : clazz.getInterfaces() ) { - found = found || isMethodDefinedInExternalInterfaceRec( clazzz, md ); - } - } - } - } - - return found; - } - - private boolean isMethodDefinedInExternalInterfaceRec( final Class clazz, final MethodDescriptor md ) { - - boolean found = false; - if ( containsNonPrivateMethod( clazz, md ) ) { - return true; - } - for ( Class clazzz : clazz.getInterfaces() ) { - found = found || isMethodDefinedInExternalInterfaceRec( clazzz, md ); - } - return found; - } - - /** - * determine wether the method md is implemented in any superclasses of class className or - * if md is declared in any interface that class className or any superclass of class - * className implements. - * - * @param origClass the orig class - * @param md the md - * @return true iff an implementation or declaration of md is found in any ancestor class/interface of class className - */ - public boolean isMethodExternallyDefined( final ClassDescriptor origClass, - final MethodDescriptor md ) { - - boolean found = false; - - found = found || isMethodExternallyDefinedRec( origClass.getSuperName(), md ); - - for ( String interfc : origClass.getInterfaces() ) { - found = found || isMethodExternallyDefinedRec( interfc, md ); - } - - List descendants = new ArrayList(); - getInternalDescendants( origClass, descendants ); - for ( ClassDescriptor cd : descendants ) { - found = found || isMethodDefinedInExternalInterface( cd, md ); - } - - return found; - } - - /** - * TODO ugly d:[ determine wether md is implemented or declared in className or any of class - * classNames ancestor classes/interfaces. - * - * - * @param className - * - * @param md - * - * @return true iff an implementation or declaration of md is found in any ancestor class/interface of - * class className - */ - private boolean isMethodExternallyDefinedRec( final String className, - final MethodDescriptor md ) { - - boolean found = false; - - if ( isClassModeled( className ) ) { - - final ClassDescriptor cd = getClassDescriptor( className ); - - boolean internallyDefined = false; -// for ( MethodDescriptor cdMd : cd.getMethods() ) { -// if ( md.overrides( cdMd ) ) internallyDefined = true; -// } - - if ( !internallyDefined ) { - - final String[] interfaces = cd.getInterfaces(); - final String superClass = cd.getSuperName(); - - if ( interfaces.length > 0 ) { - for ( int i = 0; i < interfaces.length; i++ ) { - found = found || isMethodExternallyDefinedRec( interfaces[ i ], md ); - } - } - - if ( className != "java/lang/Object" ) { // we might be analyzing rt.jar :] - found = found || isMethodExternallyDefinedRec( superClass, md ); - } - } - } else { - - Class clazz = resolve( className ); - - if ( clazz != null ) { - // ALL methods, including inherited. - - found = containsNonPrivateMethod( clazz, md ); - - if ( !found && !"java/lang/Object".equals( clazz.getName() ) ) { - - final Class superClass = clazz.getSuperclass(); - if ( superClass != null ) { - found = found || isMethodExternallyDefinedRec( superClass.getName(), md ); - } - - final Class[] interfaces = clazz.getInterfaces(); - - for ( Class interfc : interfaces ) { - found = found || isMethodExternallyDefinedRec( interfc.getName(), md ); - } - } - } - } - - return found; - } - - private boolean containsNonPrivateMethod( Class clazz, MethodDescriptor md ) { - - boolean found = false; - final Method[] methods = clazz.getDeclaredMethods(); - - for ( int i = 0; i < methods.length; i++ ) { - //final MethodDescriptor md2 = method2Descriptor( methods[ i ] ); - if ( !Modifier.isPrivate( methods[ i ].getModifiers() ) ) { - if ( md.overrides( methods[ i ] ) ) found = true; - } - } - return found; - } - - /** - * Create entry point edges. - * - * @param entryPoints the entry points - */ - public void createEntryPointEdges( List entryPoints ) { - - Object entryPointNode = getEntryPointNode(); - - for ( AbstractDescriptor descriptor : entryPoints ) { - if ( descriptor instanceof MethodDescriptor ) { - - MethodDescriptor md = (MethodDescriptor) descriptor; - - createDependencyEdge( entryPointNode, md.getNode(), EdgeType.INVOKES ); - createDependencyEdge( entryPointNode, md.getNode(), EdgeType.RESOLVE ); - } else { - createDependencyEdge( entryPointNode, descriptor.getNode(), EdgeType.ENTRYPOINT ); - } - } - } - private Class resolve( String className ) { Class clazz = null; try { @@ -731,115 +384,4 @@ private Class resolve( String className ) { return clazz; } } - - /** - * Is all resolved boolean. - * - * @return the boolean - */ - public boolean isAllResolved() { - return allResolved; - } - - /** - * convert java.lang.reflect.Method to MethodDescriptor - * - * - * @param m java Method - * - * @return a MethodDescriptor with the same properties as Method m. - */ - private MethodDescriptor method2Descriptor( final Method m, final File sourceJar ) { - - final int access = m.getModifiers(); - final String desc = Type.getMethodDescriptor( m ); - final Class[] exceptionClasses = m.getExceptionTypes(); - final String[] exceptions = new String[ exceptionClasses.length ]; - for ( int i = 0; i < exceptionClasses.length; i++ ) { - exceptions[ i ] = exceptionClasses[ i ].getName(); - } - - return new MethodDescriptor( m.getName(), access, desc, exceptions, sourceJar ); - } - - /** - * Gets node type. - * - * @param n the n - * @return the node type - */ - public int getNodeType( final Object n ) { - return (int) node2Type.get( n ); - } - - /** - * Mark obsolete. - * - * @param n the n - */ - public void markObsolete( final Object n ) { - - int type = getNodeType( n ); - if ( ! NodeType.isObsolete( type ) ) { - type += NodeType.OBSOLETE; - node2Type.put( n, type ); - } - } - - /** - * Mark not obsolete. - * - * @param n the n - */ - public void markNotObsolete( final Object n ) { - - // TODO use ~ - int type = getNodeType( n ); - if ( NodeType.isObsolete( type ) ) { - type -= NodeType.OBSOLETE; - node2Type.put( n, type ); - } - } - - /** - * Mark stub needed. - * - * @param n the n - */ - public void markStubNeeded( final Object n ) { - int type = getNodeType( n ); - if ( ! NodeType.isStubNeeded( type ) ) { - type += NodeType.STUB; - node2Type.put( n, type ); - } - } - - /** - * Is obsolete boolean. - * - * @param n the n - * @return the boolean - */ - public boolean isObsolete( final Object n ) { - return NodeType.isObsolete( (int) node2Type.get( n ) ); - } - - /** - * Is stub needed boolean. - * - * @param n the n - * @return the boolean - */ - public boolean isStubNeeded( final Object n ) { - return NodeType.isStubNeeded( (int) node2Type.get( n ) ); - } - - /** - * Gets network. - * - * @return the network - */ - public Network getNetwork() { - return network; - } } diff --git a/src/test/java/com/yworks/yguard/obf/AbstractObfuscationTest.java b/src/test/java/com/yworks/yguard/obf/AbstractObfuscationTest.java index 87987e8e..b86547e9 100644 --- a/src/test/java/com/yworks/yguard/obf/AbstractObfuscationTest.java +++ b/src/test/java/com/yworks/yguard/obf/AbstractObfuscationTest.java @@ -8,8 +8,8 @@ import java.io.InputStream; import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; +import java.util.NoSuchElementException; import com.yworks.util.Compiler; import com.yworks.util.InMemoryArchive; @@ -48,18 +48,7 @@ private Archive newArchiveImpl( ) throws IOException { final Class resolver = getClass(); - final Compiler compiler = Compiler.newCompiler(); - - final ArrayList sources = new ArrayList(); - for (TypeStruct struct : sourceFiles) { - final URL url = resolver.getResource(struct.fileName); - assertNotNull("Could not resolve " + struct.fileName + '.', url); - - sources.add(compiler.newUrlSource(struct.typeName, url)); - } - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(sources, baos); + final ByteArrayOutputStream baos = compileImpl(resolver, sourceFiles); final InMemoryArchive archive = new InMemoryArchive(name); archive.addAll(baos.toByteArray()); @@ -81,6 +70,45 @@ private Archive newArchiveImpl( return archive; } + static ByteArrayOutputStream compile( + final Class resolver, final TypeStruct... types + ) { + return compileImpl(Compiler.newCompiler(), resolver, new ArrayIterable<>(types)); + } + + static ByteArrayOutputStream compile( + final Compiler compiler, final Class resolver, final TypeStruct... types + ) { + return compileImpl(compiler, resolver, new ArrayIterable<>(types)); + } + + private static ByteArrayOutputStream compileImpl( + final Class resolver, final Iterable types + ) { + return compileImpl(Compiler.newCompiler(), resolver, types); + } + + private static ByteArrayOutputStream compileImpl( + final Compiler compiler, final Class resolver, final Iterable types + ) { + // resolve java source code and create corresponding source model items + final ArrayList sources = new ArrayList(); + + for (final TypeStruct type : types) { + final URL source = resolver.getResource(type.fileName); + assertNotNull("Could not resolve " + type.fileName + '.', source); + + sources.add(compiler.newUrlSource(type.typeName, source)); + } + + + // compile the java source code + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + compiler.compile(sources, baos); + + return baos; + } + /** * Gets major version. * @@ -163,4 +191,45 @@ static final class TypeStruct { this.typeName = typeName; } } + + private static final class ArrayIterable implements Iterable { + private final T[] items; + + ArrayIterable( T[] items ) { + this.items = items; + } + + @Override + public Iterator iterator() { + return new ArrayIterator<>(items); + } + } + + private static final class ArrayIterator implements Iterator { + private final T[] items; + private int index; + + ArrayIterator( final T[] items ) { + this.items = items; + } + + @Override + public boolean hasNext() { + return index < items.length; + } + + @Override + public T next() { + if (hasNext()) { + return items[index++]; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } } diff --git a/src/test/java/com/yworks/yguard/obf/BootstrapMethodsTest.java b/src/test/java/com/yworks/yguard/obf/BootstrapMethodsTest.java index 62dc802f..c014e325 100644 --- a/src/test/java/com/yworks/yguard/obf/BootstrapMethodsTest.java +++ b/src/test/java/com/yworks/yguard/obf/BootstrapMethodsTest.java @@ -1,6 +1,5 @@ package com.yworks.yguard.obf; -import com.yworks.util.Compiler; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -13,7 +12,6 @@ import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotNull; @@ -133,19 +131,8 @@ private void runBootstrapMethodsTest( final String fileName, final String expected ) throws Exception { - // look for java source code that will be compiled with bootstrap methods - final URL source = getClass().getResource(fileName); - assertNotNull("Could not resolve " + fileName + '.', source); - - - // compile the java source code - final com.yworks.util.Compiler compiler = Compiler.newCompiler(); - - final ArrayList sources = new ArrayList(); - sources.add(compiler.newUrlSource(testTypeName, source)); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(sources, baos); + final ByteArrayOutputStream baos = compile( + getClass(), new TypeStruct(fileName, testTypeName)); // store the resulting bytecode in temporary files and ... diff --git a/src/test/java/com/yworks/yguard/obf/KeepExtendsTest.java b/src/test/java/com/yworks/yguard/obf/KeepExtendsTest.java index 51d19be5..b4f2feb4 100644 --- a/src/test/java/com/yworks/yguard/obf/KeepExtendsTest.java +++ b/src/test/java/com/yworks/yguard/obf/KeepExtendsTest.java @@ -1,12 +1,17 @@ package com.yworks.yguard.obf; -import com.yworks.util.Compiler; -import com.yworks.yshrink.YShrinkModel; import com.yworks.common.ShrinkBag; import com.yworks.common.ant.InOutPair; +import com.yworks.yguard.ObfuscatorTask; +import com.yworks.yguard.YGuardTask; +import com.yworks.yguard.ant.ClassSection; +import com.yworks.yguard.ant.ExposeSection; +import com.yworks.yshrink.YShrinkModel; import com.yworks.yshrink.YShrinkModelImpl; -import com.yworks.yshrink.core.URLCpResolver; +import com.yworks.yshrink.core.ClassResolver; import com.yworks.yshrink.model.Model; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Target; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -15,9 +20,15 @@ import java.io.File; import java.lang.reflect.Field; import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; /** @@ -33,6 +44,93 @@ public class KeepExtendsTest extends AbstractObfuscationTest { @Rule public TestName name = new TestName(); + @Test + public void testTaskKeepsExtends() throws Exception { + assertTrue("Invalid Java version", 11 <= getMajorVersion()); + + final TypeStruct[] types = { + new TypeStruct("asm/AbstractBaseClass.txt", "com.yworks.ext.test.AbstractBaseClass"), + new TypeStruct("asm/Impl.txt", "com.yworks.impl.test.Impl"), + new TypeStruct("asm/Main.txt", "com.yworks.impl.test.Main"), + new TypeStruct("asm/Sample.txt", "com.yworks.impl.test.Sample") + }; + + final ByteArrayOutputStream baos = compile(getClass(), types); + + final File inTmp = File.createTempFile(name.getMethodName() + "_in_", ".jar"); + final File outTmp = File.createTempFile(name.getMethodName() + "_out_", ".jar"); + + try { + write(baos.toByteArray(), inTmp); + + final YGuardTask task = createTask(inTmp, outTmp); + + final ObfuscatorTask rename = task.createRename(); + final ExposeSection keep = (ExposeSection) rename.createKeep(); + final ClassSection keepClass = keep.createClass(); + keepClass.setExtends("com.yworks.ext.test.AbstractBaseClass"); + + task.execute(); + + final List entries = listClassEntries(outTmp); + assertNotNull(entries); + assertTrue("com/yworks/ext/test/AbstractBaseClass.class should exist", + entries.contains("com/yworks/ext/test/AbstractBaseClass.class")); + assertTrue("com/yworks/impl/test/Impl.class should exist", + entries.contains("com/yworks/impl/test/Impl.class")); + assertFalse("com/yworks/impl/test/Main.class should not exist", + entries.contains("com/yworks/impl/test/Main.class")); + assertFalse("com/yworks/impl/test/Sample.class should not exist", + entries.contains("com/yworks/impl/test/Sample.class")); + } finally { + // clean up and remove temporary files + inTmp.delete(); + outTmp.delete(); + } + } + + @Test + public void testTaskKeepsImplements() throws Exception { + assertTrue("Invalid Java version", 11 <= getMajorVersion()); + + final TypeStruct[] types = { + new TypeStruct("asm/AnInterface.txt", "com.yworks.ext.test.AnInterface"), + new TypeStruct("asm/AnInterfaceImpl.txt", "com.yworks.impl.test.AnInterfaceImpl"), + new TypeStruct("asm/AnInterfaceUsage.txt", "com.yworks.impl.test.AnInterfaceUsage"), + }; + + final ByteArrayOutputStream baos = compile(getClass(), types); + + final File inTmp = File.createTempFile(name.getMethodName() + "_in_", ".jar"); + final File outTmp = File.createTempFile(name.getMethodName() + "_out_", ".jar"); + + try { + write(baos.toByteArray(), inTmp); + + final YGuardTask task = createTask(inTmp, outTmp); + + final ObfuscatorTask rename = task.createRename(); + final ExposeSection keep = (ExposeSection) rename.createKeep(); + final ClassSection keepClass = keep.createClass(); + keepClass.setImplements("com.yworks.ext.test.AnInterface"); + + task.execute(); + + final List entries = listClassEntries(outTmp); + assertNotNull(entries); + assertTrue("com/yworks/ext/test/AnInterface.class should exist", + entries.contains("com/yworks/ext/test/AnInterface.class")); + assertTrue("com/yworks/impl/test/AnInterfaceImpl.class should exist", + entries.contains("com/yworks/impl/test/AnInterfaceImpl.class")); + assertFalse("com/yworks/impl/test/AnInterfaceUsage.class should not exist", + entries.contains("com/yworks/impl/test/AnInterfaceUsage.class")); + } finally { + // clean up and remove temporary files + inTmp.delete(); + outTmp.delete(); + } + } + @Test public void testExtends() throws Exception { impl(new TypeStruct("asm/AbstractBaseClass.txt", "com.yworks.ext.test.AbstractBaseClass"), @@ -61,25 +159,8 @@ public void testPermittedSubclasses() throws Exception { private void impl( final TypeStruct... types ) throws Exception { assertTrue("Invalid Java version", 11 <= getMajorVersion()); - - final com.yworks.util.Compiler compiler = Compiler.newCompiler(); - - // resolve java source code and create corresponding source model items - final ArrayList sources = new ArrayList(); - - final Class resolver = getClass(); - for (int i = 0; i < types.length; ++i) { - final URL source = resolver.getResource(types[i].fileName); - assertNotNull("Could not resolve " + types[i].fileName + '.', source); - - sources.add(compiler.newUrlSource(types[i].typeName, source)); - } - - // compile the java source code - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(sources, baos); - + final ByteArrayOutputStream baos = compile(getClass(), types); // store resulting bytecode in temporary files and ... final File inTmp = File.createTempFile(name.getMethodName() + "_in_", ".jar"); @@ -98,7 +179,7 @@ private void impl( final TypeStruct... types ) throws Exception { final YShrinkModel wrapper = new YShrinkModelImpl(); final Model model = getModel(wrapper); - model.setClassResolver(new URLCpResolver(new URL[] {inTmp.toURI().toURL()})); + model.setClassResolver(new UrlResolver(inTmp.toURI().toURL())); wrapper.createSimpleModel(bags); } finally { @@ -110,9 +191,75 @@ private void impl( final TypeStruct... types ) throws Exception { } private static Model getModel( final YShrinkModel wrapper ) throws Exception { - final Class c = wrapper.getClass(); + final Class c = wrapper.getClass(); final Field f = c.getDeclaredField("model"); f.setAccessible(true); return (Model) f.get(wrapper); } + + private static YGuardTask createTask( final File inTmp, final File outTmp ) { + final Project project = new Project(); + project.init(); + + final YGuardTask task = new YGuardTask(); + task.setProject(project); + task.setTaskType("yguard"); + task.setTaskName("yguard"); + task.setOwningTarget(new Target()); + + final ShrinkBag pair = task.createInOutPair(); + pair.setIn(inTmp); + pair.setOut(outTmp); + + return task; + } + + private static List listClassEntries( final File file ) throws Exception { + final ArrayList result = new ArrayList<>(); + try (JarFile archive = new JarFile(file)) { + for (Enumeration entries = archive.entries(); + entries.hasMoreElements();) { + final JarEntry entry = entries.nextElement(); + if (entry.isDirectory()) { + continue; + } + + final String name = entry.getName(); + if (name.toLowerCase().endsWith(".class")) { + result.add(name); + } + } + } + return result; + } + + private static final class UrlResolver implements ClassResolver { + private URLClassLoader urlClassLoader; + + UrlResolver( final URL url ) { + urlClassLoader = URLClassLoader.newInstance(new URL[]{url}); + } + + @Override + public Class resolve( final String className ) throws ClassNotFoundException { + try { + return Class.forName( className, false, urlClassLoader ); + } catch ( NoClassDefFoundError ncdfe ) { + String message = ncdfe.getMessage(); + if ( message == null || message.equals( className ) ) { + message = className; + } else { + message = message + "[" + className + "]"; + } + throw new ClassNotFoundException( message, ncdfe ); + } catch ( LinkageError le ) { + throw new ClassNotFoundException( className, le ); + } + } + + @Override + public void close() throws Exception { + urlClassLoader.close(); + } + } } diff --git a/src/test/java/com/yworks/yguard/obf/MethodParametersTest.java b/src/test/java/com/yworks/yguard/obf/MethodParametersTest.java index 9acdf7c8..a99bda0b 100644 --- a/src/test/java/com/yworks/yguard/obf/MethodParametersTest.java +++ b/src/test/java/com/yworks/yguard/obf/MethodParametersTest.java @@ -15,7 +15,6 @@ import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; -import java.net.URL; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.Map; @@ -23,7 +22,6 @@ import java.util.jar.JarEntry; import java.util.jar.JarInputStream; -import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertEquals; @@ -70,21 +68,12 @@ private void impl( final boolean retainAttr ) throws Exception { final String testTypeName = "com.yworks.yguard.obf.SimpleMethodParametersTest"; - // look for java source code that will be compiled with MethodParameters - final String fileName = "SimpleMethodParametersTest.txt"; - final URL source = getClass().getResource(fileName); - assertNotNull("Could not resolve " + fileName + '.', source); - // compile the java source code - final com.yworks.util.Compiler compiler = Compiler.newCompiler(); + final Compiler compiler = Compiler.newCompiler(); compiler.addOption("-parameters"); - - final ArrayList sources = new ArrayList(); - sources.add(compiler.newUrlSource(testTypeName, source)); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(sources, baos); + final ByteArrayOutputStream baos = compile(compiler, getClass(), + new TypeStruct("SimpleMethodParametersTest.txt", testTypeName)); // store resulting bytecode in temporary files and ... diff --git a/src/test/java/com/yworks/yshrink/ant/TestPatternMatchedSection.java b/src/test/java/com/yworks/yshrink/ant/TestPatternMatchedSection.java deleted file mode 100644 index f5e14a23..00000000 --- a/src/test/java/com/yworks/yshrink/ant/TestPatternMatchedSection.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.yworks.yshrink.ant; - -import org.junit.Test; -import org.objectweb.asm.Opcodes; -import com.yworks.common.ant.PatternMatchedSection; - -import static junit.framework.TestCase.assertFalse; -import static junit.framework.TestCase.assertTrue; - -/** - * The type Test pattern matched section. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class TestPatternMatchedSection { - /** - * Test is access level. - */ - @Test - public void testIsAccessLevel() { - assertTrue( - PatternMatchedSection.Access.PUBLIC.isAccessLevel( - Opcodes.ACC_PUBLIC ) ); - - assertFalse( - PatternMatchedSection.Access.PUBLIC.isAccessLevel( - Opcodes.ACC_PRIVATE ) ); - - assertFalse( - PatternMatchedSection.Access.PUBLIC.isAccessLevel( - Opcodes.ACC_PROTECTED ) ); - - assertFalse( - PatternMatchedSection.Access.PROTECTED.isAccessLevel( - Opcodes.ACC_PRIVATE ) ); - - assertFalse( - PatternMatchedSection.Access.PUBLIC.isAccessLevel( - PatternMatchedSection.Access.FRIENDLY ) ); - - } -} diff --git a/src/test/java/com/yworks/yshrink/java13/DefaultMethodTest.java b/src/test/java/com/yworks/yshrink/java13/DefaultMethodTest.java deleted file mode 100644 index ee5c5e94..00000000 --- a/src/test/java/com/yworks/yshrink/java13/DefaultMethodTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.yworks.yshrink.java13; - -import com.yworks.common.ant.InOutPair; -import com.yworks.yshrink.YShrink; -import com.yworks.yshrink.ant.filters.AllMainMethodsFilter; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; - -import static junit.framework.TestCase.assertNotNull; -import static org.junit.Assert.assertEquals; - -/** - * The type Default method test. - */ -public class DefaultMethodTest { - /** - * The Name. - */ - @Rule - public TestName name = new TestName(); - - /** - * Direct super interface test. - */ - @Test - public void DirectSuperInterfaceTest() { - final String testTypeName = "com.yworks.yshrink.java13.DirectSuperInterfaceTest"; - - final String fileName = "DirectSuperInterfaceTest.txt"; - final URL source = getClass().getResource(fileName); - assertNotNull("Could not resolve " + fileName + '.', source); - - - // compile the java source code - final com.yworks.util.Compiler compiler = com.yworks.util.Compiler.newCompiler(); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(Utils.listOf(compiler.newUrlSource(testTypeName, source)), baos); - - try { - // store resulting bytecode in temporary files and ... - File inTmp = File.createTempFile(name.getMethodName() + "_in_", ".jar"); - File outTmp = File.createTempFile(name.getMethodName() + "_out_", ".jar"); - Files.write(inTmp.toPath(), baos.toByteArray()); - - // Run shrinker - YShrink yShrink = new YShrink(false, "SHA-1,MD5"); - InOutPair inOutPair = new InOutPair(); - inOutPair.setIn(inTmp); - inOutPair.setOut(outTmp); - yShrink.doShrinkPairs(Utils.listOf(inOutPair), new AllMainMethodsFilter(), null); - - // load shrinked class and run test method - final ByteArrayOutputStream output = new ByteArrayOutputStream(); - final ClassLoader cl = URLClassLoader.newInstance(new URL[]{outTmp.toURI().toURL()}); - final Class shrinkedType = Class.forName(testTypeName, true, cl); - final Method run = shrinkedType.getMethod("run", PrintStream.class); - run.invoke(null, new PrintStream(output)); - - // check test method output - assertEquals( - "Wrong test output", - String.format("Hello from interface%n", System.lineSeparator()), - output.toString()); - - // clean up and remove temporary files - inTmp.delete(); - outTmp.delete(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * Simple chain test. - */ - @Test - public void SimpleChainTest() { - final String testTypeName = "com.yworks.yshrink.java13.SimpleChainTest"; - - final String fileName = "SimpleChainTest.txt"; - final URL source = getClass().getResource(fileName); - assertNotNull("Could not resolve " + fileName + '.', source); - - - // compile the java source code - final com.yworks.util.Compiler compiler = com.yworks.util.Compiler.newCompiler(); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(Utils.listOf(compiler.newUrlSource(testTypeName, source)), baos); - - try { - // store resulting bytecode in temporary files and ... - File inTmp = File.createTempFile(name.getMethodName() + "_in_", ".jar"); - File outTmp = File.createTempFile(name.getMethodName() + "_out_", ".jar"); - Files.write(inTmp.toPath(), baos.toByteArray()); - - // Run shrinker - YShrink yShrink = new YShrink(false, "SHA-1,MD5"); - InOutPair inOutPair = new InOutPair(); - inOutPair.setIn(inTmp); - inOutPair.setOut(outTmp); - yShrink.doShrinkPairs(Utils.listOf(inOutPair), new AllMainMethodsFilter(), null); - - // load shrinked class and run test method - final ByteArrayOutputStream output = new ByteArrayOutputStream(); - final ClassLoader cl = URLClassLoader.newInstance(new URL[]{outTmp.toURI().toURL()}); - final Class shrinkedType = Class.forName(testTypeName, true, cl); - final Method run = shrinkedType.getMethod("run", PrintStream.class); - run.invoke(null, new PrintStream(output)); - - // check test method output - assertEquals( - "Wrong test output", - String.format("Hello from second interface%n", System.lineSeparator()), - output.toString()); - - // clean up and remove temporary files - inTmp.delete(); - outTmp.delete(); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/src/test/java/com/yworks/yshrink/java13/InvokeDynamicTest.java b/src/test/java/com/yworks/yshrink/java13/InvokeDynamicTest.java deleted file mode 100644 index c985a668..00000000 --- a/src/test/java/com/yworks/yshrink/java13/InvokeDynamicTest.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.yworks.yshrink.java13; - -import com.yworks.common.ant.InOutPair; -import com.yworks.yshrink.YShrink; -import com.yworks.yshrink.ant.filters.AllMainMethodsFilter; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; - -import static junit.framework.TestCase.assertNotNull; -import static org.junit.Assert.assertEquals; - -/** - * The type Invoke dynamic test. - */ -public class InvokeDynamicTest { - /** - * The Name. - */ - @Rule - public TestName name = new TestName(); - - /** - * String concat factory test. - */ - @Test - public void StringConcatFactoryTest() { - // StringConcatFactory bootstrap methods are used only in Java 8 and newer - final String testTypeName = "com.yworks.yshrink.java13.StringConcatFactoryTest"; - - // look for java source code that will be compiled with StringConcatFactory - // bootstrap methods - final String fileName = "StringConcatFactoryTest.txt"; - final URL source = getClass().getResource(fileName); - assertNotNull("Could not resolve " + fileName + '.', source); - - // compile the java source code - final com.yworks.util.Compiler compiler = com.yworks.util.Compiler.newCompiler(); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(Utils.listOf(compiler.newUrlSource(testTypeName, source)), baos); - - try { - // store resulting bytecode in temporary files and ... - File inTmp = File.createTempFile(name.getMethodName() + "_in_", ".jar"); - File outTmp = File.createTempFile(name.getMethodName() + "_out_", ".jar"); - Files.write(inTmp.toPath(), baos.toByteArray()); - - // Run shrinker - YShrink yShrink = new YShrink(false, "SHA-1,MD5"); - InOutPair inOutPair = new InOutPair(); - inOutPair.setIn(inTmp); - inOutPair.setOut(outTmp); - yShrink.doShrinkPairs(Utils.listOf(inOutPair), new AllMainMethodsFilter(), null); - - // load shrinked class and run test method - final ByteArrayOutputStream output = new ByteArrayOutputStream(); - final ClassLoader cl = URLClassLoader.newInstance(new URL[]{outTmp.toURI().toURL()}); - final Class shrinkedType = Class.forName("com.yworks.yshrink.java13.StringConcatFactoryTest", true, cl); - final Method run = shrinkedType.getMethod("run", PrintStream.class); - run.invoke(null, new PrintStream(output)); - - // check test method output - assertEquals( - "Wrong test output", - String.format("hello from concat factory%n", System.lineSeparator()), - output.toString()); - - // clean up and remove temporary files - inTmp.delete(); - outTmp.delete(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * Lambda meta factory test. - */ - @Test - public void LambdaMetaFactoryTest() { - // LambdaMetaFactory bootstrap methods are used only in Java 8 and newer - final String testTypeName = "com.yworks.yshrink.java13.LambdaMetaFactoryTest"; - - final String testMethodName = "void run(java.io.PrintStream)"; - - // look for java source code that will be compiled with StringConcatFactory - // bootstrap methods - final String fileName = "LambdaMetaFactoryTest.txt"; - final URL source = getClass().getResource(fileName); - assertNotNull("Could not resolve " + fileName + '.', source); - - // compile the java source code - final com.yworks.util.Compiler compiler = com.yworks.util.Compiler.newCompiler(); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(Utils.listOf(compiler.newUrlSource(testTypeName, source)), baos); - - try { - // store resulting bytecode in temporary files and ... - File inTmp = File.createTempFile(name.getMethodName() + "_in_", ".jar"); - File outTmp = File.createTempFile(name.getMethodName() + "_out_", ".jar"); - Files.write(inTmp.toPath(), baos.toByteArray()); - - // Run shrinker - YShrink yShrink = new YShrink(false, "SHA-1,MD5"); - InOutPair inOutPair = new InOutPair(); - inOutPair.setIn(inTmp); - inOutPair.setOut(outTmp); - yShrink.doShrinkPairs(Utils.listOf(inOutPair), new AllMainMethodsFilter(), null); - - // load shrinked class and run test method - final ByteArrayOutputStream output = new ByteArrayOutputStream(); - final ClassLoader cl = URLClassLoader.newInstance(new URL[]{outTmp.toURI().toURL()}); - final Class shrinkedType = Class.forName("com.yworks.yshrink.java13.LambdaMetaFactoryTest", true, cl); - final Method run = shrinkedType.getMethod("run", PrintStream.class); - run.invoke(null, new PrintStream(output)); - - // check test method output - assertEquals( - "Wrong test output", - String.format("Hello from lambda invoke%n", System.lineSeparator()), - output.toString()); - - // clean up and remove temporary files - inTmp.delete(); - outTmp.delete(); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/src/test/java/com/yworks/yshrink/java13/NestHostTest.java b/src/test/java/com/yworks/yshrink/java13/NestHostTest.java deleted file mode 100644 index deb9e61a..00000000 --- a/src/test/java/com/yworks/yshrink/java13/NestHostTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.yworks.yshrink.java13; - -import com.yworks.common.ant.InOutPair; -import com.yworks.yshrink.YShrink; -import com.yworks.yshrink.ant.filters.AllMainMethodsFilter; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -import static junit.framework.TestCase.assertNotNull; -import static org.junit.Assert.assertEquals; - -/** - * The type Nest host test. - */ -public class NestHostTest { - /** - * The Name. - */ - @Rule - public TestName name = new TestName(); - - /** - * Test no stub created. - */ - @Test - public void TestNoStubCreated() { - // LambdaMetaFactory bootstrap methods are used only in Java 8 and newer - final String testTypeName = "com.yworks.yshrink.java13.NestHostStubTest"; - - // look for java source code that will be compiled with StringConcatFactory - // bootstrap methods - final String fileName = "NestHostStubTest.txt"; - final URL source = getClass().getResource(fileName); - assertNotNull("Could not resolve " + fileName + '.', source); - - // compile the java source code - final com.yworks.util.Compiler compiler = com.yworks.util.Compiler.newCompiler(); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - compiler.compile(Utils.listOf(compiler.newUrlSource(testTypeName, source)), baos); - - try { - // store resulting bytecode in temporary files and ... - File inTmp = File.createTempFile(name.getMethodName() + "_in_", ".jar"); - File outTmp = File.createTempFile(name.getMethodName() + "_out_", ".jar"); - Files.write(inTmp.toPath(), baos.toByteArray()); - - // Run shrinker - YShrink yShrink = new YShrink(false, "SHA-1,MD5"); - InOutPair inOutPair = new InOutPair(); - inOutPair.setIn(inTmp); - inOutPair.setOut(outTmp); - yShrink.doShrinkPairs(Utils.listOf(inOutPair), new AllMainMethodsFilter(), null); - - // load shrinked class and run test method - final ByteArrayOutputStream output = new ByteArrayOutputStream(); - final ClassLoader cl = URLClassLoader.newInstance(new URL[]{outTmp.toURI().toURL()}); - final Class shrinkedType = Class.forName("com.yworks.yshrink.java13.NestHostStubTest", true, cl); - final Method run = shrinkedType.getMethod("run", PrintStream.class); - run.invoke(null, new PrintStream(output)); - - // check test method output - assertEquals( - "Wrong test output", - String.format("Hello from nested class%n", System.lineSeparator()), - new String(output.toByteArray(), StandardCharsets.UTF_8)); - - // clean up and remove temporary files - inTmp.delete(); - outTmp.delete(); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/src/test/java/com/yworks/yshrink/java13/Utils.java b/src/test/java/com/yworks/yshrink/java13/Utils.java deleted file mode 100644 index 1b240b53..00000000 --- a/src/test/java/com/yworks/yshrink/java13/Utils.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.yworks.yshrink.java13; - -import java.util.ArrayList; -import java.util.List; - -/** - * Provides utility methods. - * - * @author Thomas Behr - */ -class Utils { - private Utils() { - } - - - static List listOf( T item ) { - final ArrayList list = new ArrayList(1); - list.add(item); - return list; - } -} diff --git a/src/test/java/com/yworks/yshrink/util/TestUtil.java b/src/test/java/com/yworks/yshrink/util/TestUtil.java deleted file mode 100644 index 8008196a..00000000 --- a/src/test/java/com/yworks/yshrink/util/TestUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.yworks.yshrink.util; - -import org.junit.Test; - -import static junit.framework.TestCase.assertEquals; - -/** - * The type Test util. - * - * @author Michael Schroeder, yWorks GmbH http://www.yworks.com - */ -public class TestUtil { - /** - * Test verbose to native type. - */ - @Test - public void testVerboseToNativeType() { - String query = "double[][][]"; - String expected = "[[[D"; - String result = Util.verboseToNativeType( query ); - assertEquals( expected, result ); - - query = "java.lang.String"; - expected = "Ljava/lang/String;"; - result = Util.verboseToNativeType( query ); - assertEquals( expected, result ); - - query = "java.lang.String[]"; - expected = "[Ljava/lang/String;"; - result = Util.verboseToNativeType( query ); - assertEquals( expected, result ); - } -} diff --git a/src/test/resources/com/yworks/yguard/obf/asm/AnInterface.txt b/src/test/resources/com/yworks/yguard/obf/asm/AnInterface.txt new file mode 100644 index 00000000..aeceddb0 --- /dev/null +++ b/src/test/resources/com/yworks/yguard/obf/asm/AnInterface.txt @@ -0,0 +1,5 @@ +package com.yworks.ext.test; + +public interface AnInterface { + public void doSomething(); +} \ No newline at end of file diff --git a/src/test/resources/com/yworks/yguard/obf/asm/AnInterfaceImpl.txt b/src/test/resources/com/yworks/yguard/obf/asm/AnInterfaceImpl.txt new file mode 100644 index 00000000..5cb486dd --- /dev/null +++ b/src/test/resources/com/yworks/yguard/obf/asm/AnInterfaceImpl.txt @@ -0,0 +1,12 @@ +package com.yworks.impl.test; + +import com.yworks.ext.test.AnInterface; + +public class AnInterfaceImpl implements AnInterface { + public AnInterfaceImpl() { + } + + public void doSomething() { + System.out.println("AnInterfaceImpl: Hello world."); + } +} diff --git a/src/test/resources/com/yworks/yguard/obf/asm/AnInterfaceUsage.txt b/src/test/resources/com/yworks/yguard/obf/asm/AnInterfaceUsage.txt new file mode 100644 index 00000000..d88625c7 --- /dev/null +++ b/src/test/resources/com/yworks/yguard/obf/asm/AnInterfaceUsage.txt @@ -0,0 +1,7 @@ +package com.yworks.impl.test; + +public class AnInterfaceUsage { + public static void main( String[] args ) { + new AnInterfaceImpl().doSomething(); + } +} \ No newline at end of file

Attribute